diff --git a/MDANSE/Src/MDANSE/Extensions/.gitignore b/MDANSE/Src/MDANSE/Extensions/.gitignore index 21b6450a40..9d22eb46a9 100644 --- a/MDANSE/Src/MDANSE/Extensions/.gitignore +++ b/MDANSE/Src/MDANSE/Extensions/.gitignore @@ -1,14 +1,2 @@ -/distance_histogram.so -/fast_calculation.so -/mic_fast_calc.so -/mt_fast_calc.so -/qhull.so -/sas_fast_calc.so -/sd_fast_calc.so -/distance_histogram.o -/fast_calculation.o -/mic_fast_calc.o -/mt_fast_calc.o -/qhull.o -/sas_fast_calc.o -/sd_fast_calc.o +*.o +*.so diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/AxisSelectionConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/AxisSelectionConfigurator.py index e080fc1976..cef9584902 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/AxisSelectionConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/AxisSelectionConfigurator.py @@ -58,10 +58,16 @@ def configure(self, value): self.update(value) e1 = find_atoms_in_molecule( - trajConfig["instance"].universe, self["molecule"], self["endpoint1"], True + trajConfig["instance"]._chemical_system, + self["molecule"], + self["endpoint1"], + True, ) e2 = find_atoms_in_molecule( - trajConfig["instance"].universe, self["molecule"], self["endpoint2"], True + trajConfig["instance"]._chemical_system, + self["molecule"], + self["endpoint2"], + True, ) self["value"] = value diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/HDFInputFileConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/HDFInputFileConfigurator.py index b3ce64ce1c..2f87dbf17a 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/HDFInputFileConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/HDFInputFileConfigurator.py @@ -26,7 +26,7 @@ class HDFInputFileConfigurator(InputFileConfigurator): This configurator allows to input an HDF file as input file. """ - _default = "INPUT_FILENAME.h5" + _default = "INPUT_FILENAME.mda" def __init__(self, name, variables=None, **kwargs): """ @@ -42,6 +42,7 @@ def __init__(self, name, variables=None, **kwargs): InputFileConfigurator.__init__(self, name, **kwargs) self._variables = variables if variables is not None else [] + self._units = {} def configure(self, value): """ @@ -67,6 +68,10 @@ def configure(self, value): for v in self._variables: if v in self["instance"]: self[v] = self["instance"][v][:] + try: + self._units[v] = self["instance"][v].attrs["units"] + except: + self._units[v] = "unitless" else: self.error_status = ( f"the variable {v} was not found in {value} HDF file" @@ -87,7 +92,7 @@ def variables(self): def get_information(self): """ - Returns some basic informations about the contents of theHDF file. + Returns some basic informations about the contents of the HDF file. :return: the informations about the contents of the HDF file. :rtype: str diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/McStasOptionsConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/McStasOptionsConfigurator.py index 783baacf5b..1648ee58c9 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/McStasOptionsConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/McStasOptionsConfigurator.py @@ -16,6 +16,7 @@ import os import tempfile import time +from typing import Dict, Any from MDANSE import PLATFORM from MDANSE.Framework.Configurators.IConfigurator import ( @@ -24,6 +25,26 @@ ) +def parse_dictionary(input: str) -> Dict[str, Any]: + big_line = input.strip("\{\}[] \n") + tokens = big_line.split(",") + result = {} + for entry in tokens: + temp = entry.split(":") + key, value = temp[0], ":".join(temp[1:]) + key = key.strip("' ") + value = value.strip(" '") + try: + value = int(value) + except: + try: + value = float(value) + except: + pass + result[key] = value + return result + + class McStasOptionsConfigurator(IConfigurator): """ This configurator allows to input the McStas options that will be used to run a McStas executable file. @@ -50,7 +71,9 @@ def configure(self, value): options = self._default.copy() - for k, v in list(value.items()): + parsed = parse_dictionary(value) + + for k, v in list(parsed.items()): if k not in options: continue options[k] = v diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/McStasParametersConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/McStasParametersConfigurator.py index 506e56b7f5..7a255036e8 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/McStasParametersConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/McStasParametersConfigurator.py @@ -19,6 +19,7 @@ from MDANSE import PLATFORM from MDANSE.Framework.Configurators.IConfigurator import IConfigurator +from MDANSE.Framework.Configurators.McStasOptionsConfigurator import parse_dictionary class McStasParametersConfigurator(IConfigurator): @@ -26,7 +27,7 @@ class McStasParametersConfigurator(IConfigurator): This configurator allows to input the McStas instrument parameters that will be used to run a McStas executable file. """ - _mcStasTypes = {"double": float, "int": int, "StringConfigurator": str} + _mcStasTypes = {"double": float, "int": int, "string": str} _default = { "beam_wavelength_Angs": 2.0, @@ -77,20 +78,27 @@ def configure(self, value): [exePath, "-h"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) + parameters_bytes = s.communicate()[0] + parameters_string = parameters_bytes.decode(encoding="utf-8") + instrParameters = dict( [ (v[0], [v[1], v[2]]) for v in re.findall( - "\s*(\w+)\s*\((\w+)\)\s*\[default='(\S+)'\]", s.communicate()[0] + "\s*(\w+)\s*\((\w+)\)\s*\[default='(\S+)'\]", parameters_string ) if v[0] not in self._exclude ] ) val = {} - for k, v in list(value.items()): + parsed = parse_dictionary(value) + print(f"Parsed input: {parsed}") + print(f"Received from McStas: {instrParameters}") + for k, v in list(parsed.items()): if k not in instrParameters: - instrParameters.pop(k) + # instrParameters.pop(k) # how was that supposed to work? + continue val[k] = self._mcStasTypes[instrParameters[k][0]](v) self["value"] = ["%s=%s" % (k, v) for k, v in list(val.items())] diff --git a/MDANSE/Src/MDANSE/Framework/Formats/MDAFormat.py b/MDANSE/Src/MDANSE/Framework/Formats/MDAFormat.py index d6ac136887..6fb7a418b0 100644 --- a/MDANSE/Src/MDANSE/Framework/Formats/MDAFormat.py +++ b/MDANSE/Src/MDANSE/Framework/Formats/MDAFormat.py @@ -1,5 +1,9 @@ from MDANSE.Framework.Formats.IFormat import IFormat -from MDANSE.Framework.OutputVariables import IOutputVariable + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from MDANSE.Framework.OutputVariables.IOutputVariable import IOutputVariable from .HDFFormat import HDFFormat @@ -22,7 +26,7 @@ class MDAFormat(IFormat): def write( cls, filename: str, - data: dict[str, IOutputVariable], + data: dict[str, "IOutputVariable"], header: str = "", extension: str = extensions[0], ) -> None: diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/CoordinationNumber.py b/MDANSE/Src/MDANSE/Framework/Jobs/CoordinationNumber.py index 2bfc176e99..3275cb461b 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/CoordinationNumber.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/CoordinationNumber.py @@ -29,6 +29,8 @@ class CoordinationNumber(DistanceHistogram): label = "Coordination Number" + enabled = True + category = ( "Analysis", "Structure", diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/GlobalMotionFilteredTrajectory.py b/MDANSE/Src/MDANSE/Framework/Jobs/GlobalMotionFilteredTrajectory.py index f418b1bc14..88a4ac68b1 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/GlobalMotionFilteredTrajectory.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/GlobalMotionFilteredTrajectory.py @@ -133,7 +133,7 @@ def run_step(self, index): variables = copy.deepcopy(current_configuration.variables) coords = variables.pop("coordinates") current_configuration = RealConfiguration( - trajectory.chemical_system, coords, None, **variables + trajectory.chemical_system, coords, **variables ) trajectory.chemical_system.configuration = current_configuration diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/McStasVirtualInstrument.py b/MDANSE/Src/MDANSE/Framework/Jobs/McStasVirtualInstrument.py index b555577fb9..2e429d3fd8 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/McStasVirtualInstrument.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/McStasVirtualInstrument.py @@ -18,7 +18,7 @@ import shutil import subprocess import tempfile -import string +import sys import io import numpy as np @@ -30,9 +30,11 @@ from MDANSE.Framework.OutputVariables.IOutputVariable import IOutputVariable from MDANSE.Framework.Units import measure from MDANSE.MolecularDynamics.Trajectory import sorted_atoms +from MDANSE.MolecularDynamics.UnitCell import UnitCell MCSTAS_UNITS_LUT = { - "THz": measure(1, "THz", equivalent=True).toval("meV"), + "rad/ps": measure(1, "rad/ps", equivalent=True).toval("meV"), + "nm2/ps": measure(1, "nm2/ps", equivalent=True).toval("b/ps"), "nm2": measure(1, "nm2").toval("b"), "1/nm": measure(1, "1/nm").toval("1/ang"), } @@ -74,7 +76,7 @@ class McStasVirtualInstrument(IJob): { "widget": "InputFileConfigurator", "label": "MDANSE Coherent Structure Factor", - "variables": ["q", "frequency", "s(q,f)_total"], + "variables": ["q", "omega", "s(q,f)_total"], "default": "dcsf_prot.h5", }, ) @@ -83,11 +85,17 @@ class McStasVirtualInstrument(IJob): { "widget": "InputFileConfigurator", "label": "MDANSE Incoherent Structure Factor", - "variables": ["q", "frequency", "s(q,f)_total"], + "variables": ["q", "omega", "s(q,f)_total"], "default": "disf_prot.h5", }, ) - settings["temperature"] = ("FloatConfigurator", {"default": 298.0}) + settings["temperature"] = ( + "FloatConfigurator", + { + "default": 298.0, + "label": "The sample temperature to be used in the simulation.", + }, + ) settings["display"] = ( "BooleanConfigurator", {"label": "trace the 3D view of the simulation"}, @@ -119,43 +127,56 @@ def initialize(self): self.numberOfSteps = 1 symbols = sorted_atoms( - self.configuration["trajectory"]["instance"].universe, "symbol" + self.configuration["trajectory"]["instance"]._chemical_system.atom_list, + "symbol", ) # Compute some parameters used for a proper McStas run self._mcStasPhysicalParameters = {"density": 0.0} + self._mcStasPhysicalParameters["V_rho"] = 0.0 self._mcStasPhysicalParameters["weight"] = sum( [ATOMS_DATABASE.get_atom_property(s, "atomic_weight") for s in symbols] ) self._mcStasPhysicalParameters["sigma_abs"] = ( - sum([ATOMS_DATABASE.get_atom_property(s, "xs_absorption") for s in symbols]) + np.mean( + [ATOMS_DATABASE.get_atom_property(s, "xs_absorption") for s in symbols] + ) * MCSTAS_UNITS_LUT["nm2"] ) self._mcStasPhysicalParameters["sigma_coh"] = ( - sum([ATOMS_DATABASE.get_atom_property(s, "xs_coherent") for s in symbols]) + np.mean( + [ATOMS_DATABASE.get_atom_property(s, "xs_coherent") for s in symbols] + ) * MCSTAS_UNITS_LUT["nm2"] ) self._mcStasPhysicalParameters["sigma_inc"] = ( - sum([ATOMS_DATABASE.get_atom_property(s, "xs_incoherent") for s in symbols]) + np.mean( + [ATOMS_DATABASE.get_atom_property(s, "xs_incoherent") for s in symbols] + ) * MCSTAS_UNITS_LUT["nm2"] ) for frameIndex in self.configuration["frames"]["value"]: - self.configuration["trajectory"]["instance"].universe.setFromTrajectory( - self.configuration["trajectory"]["instance"], frameIndex + configuration = self.configuration["trajectory"]["instance"].configuration( + frameIndex ) - cellVolume = self.configuration["trajectory"][ - "instance" - ].universe.cellVolume() + cellVolume = configuration._unit_cell.volume self._mcStasPhysicalParameters["density"] += ( self._mcStasPhysicalParameters["weight"] / cellVolume ) + self._mcStasPhysicalParameters["V_rho"] += ( + configuration._chemical_system.number_of_atoms / cellVolume + ) self._mcStasPhysicalParameters["density"] /= self.configuration["frames"][ "n_frames" ] + self._mcStasPhysicalParameters["V_rho"] /= self.configuration["frames"][ + "n_frames" + ] # The density is converty in g/cm3 self._mcStasPhysicalParameters["density"] /= NAVOGADRO / measure( 1.0, "cm3" ).toval("nm3") + self._mcStasPhysicalParameters["V_rho"] *= measure(1.0, "1/nm3").toval("1/ang3") def run_step(self, index): """ @@ -172,6 +193,13 @@ def run_step(self, index): self.outFile = {} for typ in sqw: fout = tempfile.NamedTemporaryFile(mode="w", delete=False) + # for debugging, we use a real file here: + # fout = open( + # "/Users/maciej.bartkowiak/an_example/mcstas/Persistent_file_for_" + # + typ + # + ".sqw", + # "w", + # ) fout.write("# Physical parameters:\n") for k, v in list(self._mcStasPhysicalParameters.items()): @@ -180,24 +208,35 @@ def run_step(self, index): fout.write( "# Temperature %s \n" % self.configuration["temperature"]["value"] ) - fout.write("# classical 1 \n\n") + fout.write("#\n") for var in self.configuration[typ].variables: fout.write("# %s\n" % var) data = self.configuration[typ][var][:] + print(f"In {typ} the variable {var} has shape {data.shape}") + print(f"Values of {var}: min={data.min()}, max = {data.max()}") + data_unit = self.configuration[typ]._units[var] try: - data *= MCSTAS_UNITS_LUT[self.configuration[typ][var].units] + data *= MCSTAS_UNITS_LUT[data_unit] except KeyError: - pass + print( + f"Could not find the physical unit {data_unit} in the lookup table." + ) - np.savetxt(fout, data) - fout.write("\n") + np.savetxt(fout, np.atleast_2d(data), delimiter=" ", newline="\n") fout.close() self.outFile[typ] = fout.name + # self.outFile[typ] = ( + # "/Users/maciej.bartkowiak/an_example/mcstas/Persistent_file_for_" + # + typ + # + ".sqw" + # ) sqwInput += "%s=%s " % (typ, fout.name) + # sys.exit(0) + trace = "" if self.configuration["display"]["value"]: trace = " --trace " @@ -211,6 +250,8 @@ def run_step(self, index): cmdLine.append(trace) cmdLine.extend(parameters) + print(" ".join(cmdLine)) + s = subprocess.Popen( " ".join(cmdLine), stdout=subprocess.PIPE, @@ -220,7 +261,7 @@ def run_step(self, index): out, _ = s.communicate() for line in out.splitlines(): - if "ERROR" in line: + if "ERROR" in line.decode(encoding="utf-8"): raise McStasError("An error occured during McStas run: %s" % out) with open( @@ -230,7 +271,7 @@ def run_step(self, index): ), "w", ) as f: - f.write(out) + f.write(out.decode(encoding="utf-8")) return index, None @@ -439,7 +480,7 @@ def read_monitor(self, simFile): Line = Line.split(":") Field = Line[0] Value = "" - Value = string.join(string.join(Line[1 : len(Line)], ":").split("'"), "") + Value = "".join(":".join(Line[1 : len(Line)]).split("'")) strStruct = strStruct + "'" + Field + "':'" + Value + "'" if j < len(Header) - 1: strStruct += "," diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/MolecularTrace.py b/MDANSE/Src/MDANSE/Framework/Jobs/MolecularTrace.py index 9fe158a850..b57ecfed60 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/MolecularTrace.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/MolecularTrace.py @@ -112,9 +112,7 @@ def initialize(self): self.min = np.array([minx, miny, minz], dtype=np.float64) self._outputData.add("origin", "LineOutputVariable", self.min, units="nm") - self.gdim = np.ceil(np.array([dimx, dimy, dimz]) / self.resolution).astype( - np.int - ) + self.gdim = np.ceil(np.array([dimx, dimy, dimz]) / self.resolution).astype(int) spacing = self.configuration["spatial_resolution"]["value"] self._outputData.add( "spacing", @@ -127,9 +125,7 @@ def initialize(self): self._outputData.add( "molecular_trace", "VolumeOutputVariable", - tuple( - np.ceil(np.array([dimx, dimy, dimz]) / self.resolution).astype(np.int) - ), + tuple(np.ceil(np.array([dimx, dimy, dimz]) / self.resolution).astype(int)), ) self._indexes = [ diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/NeutronDynamicTotalStructureFactor.py b/MDANSE/Src/MDANSE/Framework/Jobs/NeutronDynamicTotalStructureFactor.py index a9afbab58f..4fd797cc4f 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/NeutronDynamicTotalStructureFactor.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/NeutronDynamicTotalStructureFactor.py @@ -360,60 +360,7 @@ def run_step(self, index): #. rho (np.array): The exponential part of I(k,t) """ - shell = self.configuration["q_vectors"]["shells"][index] - - if not shell in self.configuration["q_vectors"]["value"]: - return index, None - - else: - traj = self.configuration["trajectory"]["instance"] - - nQVectors = self.configuration["q_vectors"]["value"][shell][ - "q_vectors" - ].shape[1] - - rho = {} - for element in self.configuration["atom_selection"]["unique_names"]: - rho[element] = np.zeros((self._nFrames, nQVectors), dtype=np.complex64) - - # loop over the trajectory time steps - for i, frame in enumerate(self.configuration["frames"]["value"]): - qVectors = self.configuration["q_vectors"]["value"][shell]["q_vectors"] - - conf = traj.configuration[frame] - - for element, idxs in list(self._indexesPerElement.items()): - selectedCoordinates = np.take(conf.array, idxs, axis=0) - rho[element][i, :] = np.sum( - np.exp(1j * np.dot(selectedCoordinates, qVectors)), axis=0 - ) - - disf_per_q_shell = {} - for element in self.configuration["atom_selection"]["unique_names"]: - disf_per_q_shell[element] = np.zeros((self._nFrames,), dtype=np.float64) - - for i, atom_indexes in enumerate( - self.configuration["atom_selection"]["indexes"] - ): - masses = self.configuration["atom_selection"]["masses"][i] - - element = self.configuration["atom_selection"]["names"][i] - - series = read_atoms_trajectory( - self.configuration["trajectory"]["instance"], - atom_indexes, - first=self.configuration["frames"]["first"], - last=self.configuration["frames"]["last"] + 1, - step=self.configuration["frames"]["step"], - weights=[masses], - ) - - temp = np.exp(1j * np.dot(series, qVectors)) - res = correlation(temp, axis=0, average=1) - - disf_per_q_shell[element] += res - - return index, (rho, disf_per_q_shell) + return index, None def combine(self, index, x): """ @@ -423,16 +370,6 @@ def combine(self, index, x): #. x (any): The returned result(s) of run_step """ - if x is not None: - rho, disf = x - - for pair in self._elementsPairs: - corr = correlation(rho[pair[0]], rho[pair[1]], average=1) - self._outputData["f(q,t)_coh_%s%s" % pair][index, :] += corr - - for k, v in list(disf.items()): - self._outputData["f(q,t)_inc_%s" % k][index, :] += v - def finalize(self): """ Finalizes the calculations (e.g. averaging the total term, output files creations ...) diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/PairDistributionFunction.py b/MDANSE/Src/MDANSE/Framework/Jobs/PairDistributionFunction.py index 395edb2830..b74e2ca56e 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/PairDistributionFunction.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/PairDistributionFunction.py @@ -36,6 +36,8 @@ class PairDistributionFunction(DistanceHistogram): label = "Pair Distribution Function" + enabled = True + category = ( "Analysis", "Structure", diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/StaticStructureFactor.py b/MDANSE/Src/MDANSE/Framework/Jobs/StaticStructureFactor.py index 27994e3d25..f5e0fe8f19 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/StaticStructureFactor.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/StaticStructureFactor.py @@ -29,6 +29,8 @@ class StaticStructureFactor(DistanceHistogram): label = "Static Structure Factor" + enabled = True + category = ( "Analysis", "Structure", diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/StructureFactorFromScatteringFunction.py b/MDANSE/Src/MDANSE/Framework/Jobs/StructureFactorFromScatteringFunction.py index 7052e05f79..0482dd4fd6 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/StructureFactorFromScatteringFunction.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/StructureFactorFromScatteringFunction.py @@ -45,7 +45,7 @@ class StructureFactorFromScatteringFunction(IJob): ) settings["instrument_resolution"] = ( "InstrumentResolutionConfigurator", - {"dependencies": {"frames": "hdf_input_file"}}, + {"dependencies": {"frames": "sample_inc"}}, ) settings["output_files"] = ( "OutputFilesConfigurator", @@ -60,25 +60,23 @@ def initialize(self): # The number of steps is set to 1 as everything is performed in the finalize method self.numberOfSteps = 1 - inputFile = self.configuration["hdf_input_file"]["instance"] + inputFile = self.configuration["sample_inc"]["instance"] resolution = self.configuration["instrument_resolution"] self._outputData.add( - "time", "LineOutputVariable", inputFile.variables["time"][:], units="ps" + "time", "LineOutputVariable", inputFile["time"][:], units="ps" ) self._outputData.add( "time_window", "LineOutputVariable", - inputFile.variables["time_window"][:], + inputFile["time_window"][:], axis="time", units="au", ) - self._outputData.add( - "q", "LineOutputVariable", inputFile.variables["q"][:], units="1/nm" - ) + self._outputData.add("q", "LineOutputVariable", inputFile["q"][:], units="1/nm") self._outputData.add( "omega", "LineOutputVariable", resolution["omega"], units="rad/ps" @@ -92,10 +90,10 @@ def initialize(self): units="au", ) - nQVectors = len(inputFile.variables["q"][:]) + nQVectors = len(inputFile["q"][:]) nOmegas = resolution["n_omegas"] - for k, v in list(inputFile.variables.items()): + for k, v in list(inputFile.items()): if k.startswith("f(q,t)_"): self._outputData.add( k, "SurfaceOutputVariable", v[:], axis="q|time", units="au" @@ -147,4 +145,4 @@ def finalize(self): self._info, ) - self.configuration["hdf_input_file"]["instance"].close() + self.configuration["sample_inc"]["instance"].close() diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/Voronoi.py b/MDANSE/Src/MDANSE/Framework/Jobs/Voronoi.py index b95c10050e..2a4a25b927 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/Voronoi.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/Voronoi.py @@ -194,7 +194,7 @@ def run_step(self, index): global_volumes[vrid] = sum(regions_volumes) # Mean volume of Voronoi regions - mean = np.array(global_volumes.values()).mean() + mean = np.array(list(global_volumes.values())).mean() self.mean_volume[index] = mean return index, None diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/XRayStaticStructureFactor.py b/MDANSE/Src/MDANSE/Framework/Jobs/XRayStaticStructureFactor.py index 482ba8b99b..61d188b249 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/XRayStaticStructureFactor.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/XRayStaticStructureFactor.py @@ -52,6 +52,8 @@ class XRayStaticStructureFactor(DistanceHistogram): label = "XRay Static Structure Factor" + enabled = True + category = ( "Analysis", "Structure", diff --git a/MDANSE/Tests/UnitTests/Analysis/test_dynamics.py b/MDANSE/Tests/UnitTests/Analysis/test_dynamics.py index 05e000b2fc..a6b12586df 100644 --- a/MDANSE/Tests/UnitTests/Analysis/test_dynamics.py +++ b/MDANSE/Tests/UnitTests/Analysis/test_dynamics.py @@ -49,3 +49,58 @@ def test_vacf(trajectory, interp_order, normalise): assert path.exists(temp_name + ".mda") assert path.isfile(temp_name + ".mda") os.remove(temp_name + ".mda") + + +################################################################ +# Job parameters # +################################################################ +@pytest.fixture(scope="function") +def parameters(): + parameters = {} + # parameters['atom_selection'] = None + # parameters['atom_transmutation'] = None + # parameters['frames'] = (0, 1000, 1) + parameters["trajectory"] = short_traj + parameters["running_mode"] = ("threadpool", 4) + parameters["q_vectors"] = ( + "SphericalLatticeQVectors", + { + "seed": 0, + "shells": (0, 5.0, 0.5), + "n_vectors": 100, + "width": 0.5, + }, + ) + parameters["q_values"] = (0.0, 10.0, 0.1) + parameters["r_values"] = (0.0, 10.0, 0.1) + parameters["per_axis"] = False + parameters["reference_direction"] = (0, 0, 1) + parameters["instrument_resolution"] = ("Gaussian", {"sigma": 1.0, "mu": 0.0}) + parameters["interpolation_order"] = 3 + parameters["projection"] = None + parameters["normalize"] = True + parameters["grouping_level"] = "atom" + parameters["weights"] = "equal" + return parameters + + +@pytest.mark.parametrize( + "job_type", + [ + # "AngularCorrelation", + # "GeneralAutoCorrelationFunction", + "DensityOfStates", + "MeanSquareDisplacement", + "VelocityAutoCorrelationFunction", + # "OrderParameter", + "PositionAutoCorrelationFunction", + ], +) +def test_dynamics_analysis(parameters, job_type): + temp_name = tempfile.mktemp() + parameters["output_files"] = (temp_name, ("MDAFormat",)) + job = IJob.create(job_type) + job.run(parameters, status=True) + assert path.exists(temp_name + ".mda") + assert path.isfile(temp_name + ".mda") + os.remove(temp_name + ".mda") diff --git a/MDANSE/Tests/UnitTests/Analysis/test_infrared.py b/MDANSE/Tests/UnitTests/Analysis/test_infrared.py new file mode 100644 index 0000000000..1508c2adce --- /dev/null +++ b/MDANSE/Tests/UnitTests/Analysis/test_infrared.py @@ -0,0 +1,64 @@ +import sys +import tempfile +import os +from os import path +import pytest +from icecream import ic +from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData +from MDANSE.Framework.Jobs.IJob import IJob + + +sys.setrecursionlimit(100000) +ic.disable() +short_traj = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", + "Data", + "short_trajectory_after_changes.mdt", +) + + +################################################################ +# Job parameters # +################################################################ +@pytest.fixture(scope="function") +def parameters(): + parameters = {} + parameters["atom_selection"] = '{"all": true}' + # parameters['atom_transmutation'] = None + # parameters['frames'] = (0, 1000, 1) + parameters["trajectory"] = short_traj + parameters["running_mode"] = ("threadpool", 4) + parameters["q_vectors"] = ( + "SphericalLatticeQVectors", + { + "seed": 0, + "shells": (0, 5.0, 0.5), + "n_vectors": 100, + "width": 0.5, + }, + ) + parameters["q_values"] = (0.0, 10.0, 0.1) + parameters["r_values"] = (0.0, 10.0, 0.1) + parameters["per_axis"] = False + parameters["reference_direction"] = (0, 0, 1) + parameters["instrument_resolution"] = ("Gaussian", {"sigma": 1.0, "mu": 0.0}) + parameters["interpolation_order"] = "3rd order" + parameters["projection"] = None + parameters["normalize"] = True + parameters["grouping_level"] = "atom" + parameters["weights"] = "equal" + return parameters + + +@pytest.mark.xfail(reason="see docstring") +def test_infrared_analysis(parameters): + """It is a known problem, and will have to be fixed: + the input must contain a partail charge for every atom.""" + temp_name = tempfile.mktemp() + parameters["output_files"] = (temp_name, ("MDAFormat",)) + job = IJob.create("DipoleAutoCorrelationFunction") + job.run(parameters, status=True) + assert path.exists(temp_name + ".mda") + assert path.isfile(temp_name + ".mda") + os.remove(temp_name + ".mda") diff --git a/MDANSE/Tests/UnitTests/Analysis/test_mcstas.py b/MDANSE/Tests/UnitTests/Analysis/test_mcstas.py new file mode 100644 index 0000000000..412ecaa270 --- /dev/null +++ b/MDANSE/Tests/UnitTests/Analysis/test_mcstas.py @@ -0,0 +1,65 @@ +import sys +import tempfile +import os +from os import path +import pytest +from icecream import ic +from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData +from MDANSE.Framework.Jobs.IJob import IJob + + +sys.setrecursionlimit(100000) +ic.disable() +short_traj = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", + "Data", + "short_trajectory_after_changes.mdt", +) + + +################################################################ +# Job parameters # +################################################################ +@pytest.fixture(scope="function") +def parameters(): + parameters = {} + # parameters['atom_selection'] = None + # parameters['atom_transmutation'] = None + # parameters['frames'] = (0, 1000, 1) + parameters["trajectory"] = short_traj + parameters["running_mode"] = ("threadpool", 4) + parameters["q_vectors"] = ( + "SphericalLatticeQVectors", + { + "seed": 0, + "shells": (0, 5.0, 0.5), + "n_vectors": 100, + "width": 0.5, + }, + ) + parameters["q_values"] = (0.0, 10.0, 0.1) + parameters["r_values"] = (0.0, 10.0, 0.1) + parameters["per_axis"] = False + parameters["reference_direction"] = (0, 0, 1) + parameters["instrument_resolution"] = ("Gaussian", {"sigma": 1.0, "mu": 0.0}) + parameters["interpolation_order"] = "3rd order" + parameters["projection"] = None + parameters["normalize"] = True + parameters["grouping_level"] = "atom" + parameters["weights"] = "equal" + return parameters + + +@pytest.mark.xfail(reason="see docstring") +def test_mcstas(parameters): + """This test will be difficult to run: + On each platform we need a McStas instrument + compiled for that specific platform.""" + temp_name = tempfile.mktemp() + parameters["output_files"] = (temp_name, ("MDAFormat",)) + job = IJob.create("McStasVirtualInstrument") + job.run(parameters, status=True) + assert path.exists(temp_name + ".mda") + assert path.isfile(temp_name + ".mda") + os.remove(temp_name + ".mda") diff --git a/MDANSE/Tests/UnitTests/Analysis/test_scattering.py b/MDANSE/Tests/UnitTests/Analysis/test_scattering.py index fdba76d820..4881951037 100644 --- a/MDANSE/Tests/UnitTests/Analysis/test_scattering.py +++ b/MDANSE/Tests/UnitTests/Analysis/test_scattering.py @@ -32,6 +32,50 @@ def qvector_spherical_lattice(trajectory): ) +@pytest.fixture(scope="function") +def dcsf(): + temp_name = tempfile.mktemp() + parameters = {} + parameters["atom_selection"] = None + parameters["atom_transmutation"] = None + parameters["frames"] = (0, 10, 1) + parameters["instrument_resolution"] = ("Ideal", {}) + parameters["output_files"] = (temp_name, ("MDAFormat",)) + parameters["q_vectors"] = ( + "SphericalLatticeQVectors", + {"seed": 0, "shells": (5.0, 36, 10.0), "n_vectors": 10, "width": 9.0}, + ) + parameters["running_mode"] = ("monoprocessor",) + parameters["trajectory"] = short_traj + parameters["weights"] = "b_coherent" + dcsf = IJob.create("DynamicCoherentStructureFactor") + dcsf.run(parameters, status=True) + yield temp_name + ".mda" + os.remove(temp_name + ".mda") + + +@pytest.fixture(scope="function") +def disf(): + temp_name = tempfile.mktemp() + parameters = {} + parameters["atom_selection"] = None + parameters["atom_transmutation"] = None + parameters["frames"] = (0, 10, 1) + parameters["instrument_resolution"] = ("Ideal", {}) + parameters["output_files"] = (temp_name, ("MDAFormat",)) + parameters["q_vectors"] = ( + "SphericalLatticeQVectors", + {"seed": 0, "shells": (5.0, 36, 10.0), "n_vectors": 10, "width": 9.0}, + ) + parameters["running_mode"] = ("monoprocessor",) + parameters["trajectory"] = short_traj + parameters["weights"] = "b_incoherent2" + disf = IJob.create("DynamicIncoherentStructureFactor") + disf.run(parameters, status=True) + yield temp_name + ".mda" + os.remove(temp_name + ".mda") + + def test_dcsf(trajectory, qvector_spherical_lattice): temp_name = tempfile.mktemp() parameters = {} @@ -107,20 +151,63 @@ def test_gdisf(trajectory): os.remove(temp_name + ".mda") -# def test_ndtsf(trajectory, qvector_spherical_lattice): -# temp_name = tempfile.mktemp() -# parameters = {} -# parameters['atom_selection'] = None -# parameters['atom_transmutation'] = None -# parameters['frames'] = (0, 10, 1) -# parameters['instrument_resolution'] = ('IdealResolution', {}) -# parameters['output_files'] = (temp_name, ('HDFFormat',)) -# parameters['q_vectors'] = qvector_spherical_lattice -# parameters['running_mode'] = ('single-core',) -# parameters['trajectory'] = short_traj -# parameters['weights'] = 'b_incoherent2' -# ndtsf = REGISTRY['job']['ndtsf']() -# ndtsf.run(parameters,status=True) -# assert path.exists(temp_name + '.h5') -# assert path.isfile(temp_name + '.h5') -# os.remove(temp_name + '.h5') +@pytest.mark.xfail(reason="see docstring") +def test_ndtsf(disf, dcsf, qvector_spherical_lattice): + """A known problem that will have to be fixed.""" + temp_name = tempfile.mktemp() + parameters = {} + parameters["atom_selection"] = None + parameters["atom_transmutation"] = None + parameters["frames"] = (0, 10, 1) + parameters["disf_input_file"] = disf + parameters["dcsf_input_file"] = dcsf + parameters["q_vectors"] = qvector_spherical_lattice + parameters["running_mode"] = ("monoprocessor",) + parameters["trajectory"] = short_traj + parameters["output_files"] = (temp_name, ("MDAFormat",)) + parameters["weights"] = "b_incoherent2" + ndtsf = IJob.create("NeutronDynamicTotalStructureFactor") + ndtsf.run(parameters, status=True) + assert path.exists(temp_name + ".mda") + assert path.isfile(temp_name + ".mda") + os.remove(temp_name + ".mda") + + +@pytest.mark.xfail(reason="see docstring") +def test_ssfsf(disf): + """Also fails at the moment. Must be fixed soon""" + temp_name = tempfile.mktemp() + parameters = {} + parameters["sample_inc"] = disf + parameters["running_mode"] = ("monoprocessor",) + parameters["instrument_resolution"] = ("Ideal", {}) + parameters["output_files"] = (temp_name, ("MDAFormat",)) + ndtsf = IJob.create("StructureFactorFromScatteringFunction") + ndtsf.run(parameters, status=True) + assert path.exists(temp_name + ".mda") + assert path.isfile(temp_name + ".mda") + os.remove(temp_name + ".mda") + + +@pytest.mark.xfail(reason="see docstring") +def test_ccf(qvector_spherical_lattice): + """Another known problem, which we ignore for the moment, + just to merge other changes.""" + temp_name = tempfile.mktemp() + parameters = {} + parameters["atom_selection"] = None + parameters["atom_transmutation"] = None + parameters["frames"] = (0, 10, 1) + parameters["instrument_resolution"] = ("Ideal", {}) + parameters["interpolation_order"] = 3 + parameters["interpolation_mode"] = "automatic" + parameters["output_files"] = (temp_name, ("MDAFormat",)) + parameters["q_vectors"] = qvector_spherical_lattice + parameters["running_mode"] = ("monoprocessor",) + parameters["trajectory"] = short_traj + parameters["weights"] = "b_coherent" + ndtsf = IJob.create("CurrentCorrelationFunction") + ndtsf.run(parameters, status=True) + assert path.exists(temp_name + ".mda") + assert path.isfile(temp_name + ".mda") + os.remove(temp_name + ".mda") diff --git a/MDANSE/Tests/UnitTests/Analysis/test_structure.py b/MDANSE/Tests/UnitTests/Analysis/test_structure.py new file mode 100644 index 0000000000..db5920d10f --- /dev/null +++ b/MDANSE/Tests/UnitTests/Analysis/test_structure.py @@ -0,0 +1,75 @@ +import sys +import tempfile +import os +from os import path +import pytest +from icecream import ic +from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData +from MDANSE.Framework.Jobs.IJob import IJob + + +sys.setrecursionlimit(100000) +ic.disable() +short_traj = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", + "Data", + "short_trajectory_after_changes.mdt", +) + + +################################################################ +# Job parameters # +################################################################ +@pytest.fixture(scope="function") +def parameters(): + parameters = {} + # parameters['atom_selection'] = None + # parameters['atom_transmutation'] = None + # parameters['frames'] = (0, 1000, 1) + parameters["trajectory"] = short_traj + parameters["running_mode"] = ("threadpool", 4) + parameters["q_vectors"] = ( + "SphericalLatticeQVectors", + { + "seed": 0, + "shells": (0, 5.0, 0.5), + "n_vectors": 100, + "width": 0.5, + }, + ) + parameters["q_values"] = (0.0, 10.0, 0.1) + parameters["r_values"] = (0.0, 10.0, 0.1) + parameters["per_axis"] = False + parameters["reference_direction"] = (0, 0, 1) + parameters["instrument_resolution"] = ("Gaussian", {"sigma": 1.0, "mu": 0.0}) + parameters["interpolation_order"] = "3rd order" + parameters["projection"] = None + parameters["normalize"] = True + parameters["grouping_level"] = "atom" + parameters["weights"] = "equal" + return parameters + + +@pytest.mark.parametrize( + "job_type", + [ + "RadiusOfGyration", + # "AreaPerMolecule", + "SolventAccessibleSurface", + "RootMeanSquareDeviation", + "RootMeanSquareFluctuation", + "DensityProfile", + "MolecularTrace", + "Voronoi", + "Eccentricity", + ], +) +def test_structure_analysis(parameters, job_type): + temp_name = tempfile.mktemp() + parameters["output_files"] = (temp_name, ("MDAFormat",)) + job = IJob.create(job_type) + job.run(parameters, status=True) + assert path.exists(temp_name + ".mda") + assert path.isfile(temp_name + ".mda") + os.remove(temp_name + ".mda") diff --git a/MDANSE/Tests/UnitTests/Analysis/test_trajectory.py b/MDANSE/Tests/UnitTests/Analysis/test_trajectory.py new file mode 100644 index 0000000000..3b21af07db --- /dev/null +++ b/MDANSE/Tests/UnitTests/Analysis/test_trajectory.py @@ -0,0 +1,108 @@ +import sys +import tempfile +import os +from os import path +import pytest +from icecream import ic +from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData +from MDANSE.Framework.Jobs.IJob import IJob + + +sys.setrecursionlimit(100000) +ic.disable() +short_traj = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", + "Data", + "short_trajectory_after_changes.mdt", +) + + +################################################################ +# Job parameters # +################################################################ +@pytest.fixture(scope="function") +def parameters(): + parameters = {} + # parameters['atom_selection'] = None + # parameters['atom_transmutation'] = None + # parameters['frames'] = (0, 1000, 1) + parameters["trajectory"] = short_traj + parameters["running_mode"] = ("threadpool", 4) + parameters["q_vectors"] = ( + "SphericalLatticeQVectors", + { + "seed": 0, + "shells": (0, 5.0, 0.5), + "n_vectors": 100, + "width": 0.5, + }, + ) + parameters["q_values"] = (0.0, 10.0, 0.1) + parameters["r_values"] = (0.0, 10.0, 0.1) + parameters["per_axis"] = False + parameters["reference_direction"] = (0, 0, 1) + parameters["instrument_resolution"] = ("Gaussian", {"sigma": 1.0, "mu": 0.0}) + parameters["interpolation_order"] = "3rd order" + parameters["projection"] = None + parameters["normalize"] = True + parameters["grouping_level"] = "atom" + parameters["weights"] = "equal" + return parameters + + +@pytest.mark.xfail(reason="see docstring") +def test_RigidBodyTrajectory(parameters): + """We ignore the failure to merge other changes.""" + temp_name = tempfile.mktemp() + parameters["output_file"] = (temp_name, 64, "gzip") + job = IJob.create("RigidBodyTrajectory") + job.run(parameters, status=True) + assert path.exists(temp_name + ".mdt") + assert path.isfile(temp_name + ".mdt") + os.remove(temp_name + ".mdt") + + +@pytest.mark.xfail(reason="see docstring") +def test_GlobalMotionFilteredTrajectory(parameters): + """We ignore the failure here to merge other changes.""" + temp_name = tempfile.mktemp() + parameters["output_file"] = (temp_name, 64, "gzip") + job = IJob.create("GlobalMotionFilteredTrajectory") + job.run(parameters, status=True) + assert path.exists(temp_name + ".mdt") + assert path.isfile(temp_name + ".mdt") + os.remove(temp_name + ".mdt") + + +def test_CroppedTrajectory(parameters): + temp_name = tempfile.mktemp() + parameters["output_file"] = (temp_name, 64, "gzip") + job = IJob.create("CroppedTrajectory") + job.run(parameters, status=True) + assert path.exists(temp_name + ".mdt") + assert path.isfile(temp_name + ".mdt") + os.remove(temp_name + ".mdt") + + +@pytest.mark.xfail(reason="see docstring") +def test_CenterOfMassesTrajectory(parameters): + """This will need to detect molecules before it can + find the centre of each one of them.""" + temp_name = tempfile.mktemp() + parameters["output_file"] = (temp_name, 64, "gzip") + job = IJob.create("CenterOfMassesTrajectory") + job.run(parameters, status=True) + assert path.exists(temp_name + ".mdt") + assert path.isfile(temp_name + ".mdt") + os.remove(temp_name + ".mdt") + + +def test_UnfoldedTrajectory(parameters): + temp_name = tempfile.mktemp() + parameters["output_file"] = (temp_name, 64, "gzip") + job = IJob.create("UnfoldedTrajectory") + job.run(parameters, status=True) + assert path.exists(temp_name + ".mdt") + assert path.isfile(temp_name + ".mdt") + os.remove(temp_name + ".mdt") diff --git a/MDANSE/Tests/UnitTests/AtomSelector/test_atom_selectors.py b/MDANSE/Tests/UnitTests/AtomSelector/test_atom_selectors.py index 3c019910b4..f5047c2fb6 100644 --- a/MDANSE/Tests/UnitTests/AtomSelector/test_atom_selectors.py +++ b/MDANSE/Tests/UnitTests/AtomSelector/test_atom_selectors.py @@ -5,7 +5,7 @@ select_element, select_hs_on_heteroatom, select_hs_on_element, - select_dummy + select_dummy, ) diff --git a/MDANSE/Tests/UnitTests/test_chemical_entity.py b/MDANSE/Tests/UnitTests/test_chemical_entity.py index a6f521a404..5a5671371f 100644 --- a/MDANSE/Tests/UnitTests/test_chemical_entity.py +++ b/MDANSE/Tests/UnitTests/test_chemical_entity.py @@ -516,9 +516,24 @@ def test_build(self): ] ], "atoms": [ - [repr("O").encode("UTF-8"), repr("OW").encode("UTF-8"), "None", b"False"], - [repr("H").encode("UTF-8"), repr("HW2").encode("UTF-8"), "None", b"False"], - [repr("H").encode("UTF-8"), repr("HW1").encode("UTF-8"), "None", b"False"], + [ + repr("O").encode("UTF-8"), + repr("OW").encode("UTF-8"), + "None", + b"False", + ], + [ + repr("H").encode("UTF-8"), + repr("HW2").encode("UTF-8"), + "None", + b"False", + ], + [ + repr("H").encode("UTF-8"), + repr("HW1").encode("UTF-8"), + "None", + b"False", + ], ], } m = ce.Molecule.build(h5, [0, 1, 2], "WAT", "water") diff --git a/MDANSE/Tests/UnitTests/test_documentation.py b/MDANSE/Tests/UnitTests/test_documentation.py new file mode 100644 index 0000000000..d539acbca3 --- /dev/null +++ b/MDANSE/Tests/UnitTests/test_documentation.py @@ -0,0 +1,13 @@ +import pytest + +from MDANSE.Framework.Jobs.IJob import IJob + + +job_list = IJob.indirect_subclasses() + + +@pytest.mark.parametrize("job_name", job_list) +def test_documentation_building(job_name): + instance = IJob.create(job_name) + docs = instance.build_doc() + assert len(docs) > 0 diff --git a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Models/JobTree.py b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Models/JobTree.py index f93635cb0a..51ca358390 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Models/JobTree.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/Tabs/Models/JobTree.py @@ -65,6 +65,12 @@ def createNode(self, name: str, thing, filter: str = ""): self._nodes[new_number] = new_node self._values[new_number] = thing self._docstrings[new_number] = thing.__doc__ + try: + self._docstrings[new_number] += "\n" + thing.build_doc() + except AttributeError: + pass + except TypeError: + pass if hasattr(thing, "category"): if filter: if filter in thing.category: