Skip to content

Commit

Permalink
Merge pull request #573 from ISISNeutronMuon/beta-release-2024
Browse files Browse the repository at this point in the history
Beta release 2024
  • Loading branch information
MBartkowiakSTFC authored Oct 15, 2024
2 parents f11a6b4 + dd68dd4 commit 9b82b6d
Show file tree
Hide file tree
Showing 38 changed files with 261 additions and 86 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
version 2.0.0b1 - 2024-10-15
--------------------------
* ADDED van Hove functions (self function and distinct function)
* FIXED Eccentricity analysis to give correct results
* ADDED Infrared analysis (only for molecules at the moment)
* FIXED MolecularTrace analysis data axes to make data plottable
* CHANGED the plotting interface to allow overplotting curves
* ADDED instrument profiles for storing user parameters
* ADDED logging in the code and a logger tab in the GUI
* ADDED charge information to the stored trajectory data
* ADDED support for H5MD trajectories

version 2.0.0 - 2024-02-15
--------------------------
* CHANGED programming language to Python 3
Expand Down
2 changes: 1 addition & 1 deletion Doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
# project = 'MDANSE'
# copyright = '2015-2022, Eric Pellegrini'
author = 'Eric Pellegrini'
release = '2.0.0a1'
release = '2.0.0b1'
version = '2.0'

# -- General configuration ---------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions Doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Welcome to MDANSE's documentation!
.. note::
This is the documentation of the MDANSE 2.0 release.
The documentation, just like the code itself, is still under development.
MDANSE 2 has currently (October 2024) just reached the first beta release.

**Useful links**: `MDANSE Project Website <https://www.isis.stfc.ac.uk/Pages/MDANSEproject.aspx>`_ | `MDANSE GitHub Page <https://github.com/ISISNeutronMuon/MDANSE>`_

Expand Down
41 changes: 23 additions & 18 deletions Doc/pages/H_start.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ Use `pip` to install the MDANSE package from the specified GitHub repository:
pip install MDANSE
The MDANSE package contains all the code needed to perform trajectory conversion
and analysis using MDANSE, but none of the visualisation tools.

Install MDANSE_GUI Package
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand All @@ -66,14 +69,9 @@ Similarly, install the MDANSE_GUI package using `pip`:
pip install MDANSE_GUI
Run MDANSE
~~~~~~~~~~

You can now start using MDANSE by running the following command:

.. code-block:: bash
mdanse_gui
From now on, the `mdanse_gui` command will be available to start
the graphical interface of MDANSE, which makes it easier to create
valid inputs for different analysis types.

Run MDANSE
~~~~~~~~~~
Expand All @@ -84,13 +82,20 @@ You can now start using MDANSE by running the following command:
mdanse_gui
This will launch the MDANSE Graphical User Interface (GUI), and you can start using MDANSE for your
analysis.

Note for Windows Users: On Windows, the command to run MDANSE may need to be:

.. code-block:: bash
python3 mdanse_gui
That's it! You have successfully installed MDANSE and are ready to use it for your data analysis needs.
This will launch the MDANSE Graphical User Interface (GUI),
and you can start using MDANSE for your analysis.

MDANSE Scripts
~~~~~~~~~~~~~~

If you intend to run your analysis on a remote platform
(e.g. a cluster), most likely you will have limited options
of using the GUI there. However, you can still prepare
a script using MDANSE_GUI on your own computer, save it
and transfer it to the other computer to run the analysis
there. You will need to change the file paths in the script,
but all the other parameters should be transferable. One
of the design principles of MDANSE 2 is that the scripts
should not depend on any settings stored locally on
a specific computer, but should instead contain all the
information needed to run a specific analysis type.
Binary file removed MDANSE/Doc/_static/mdanse_logo.png
Binary file not shown.
2 changes: 1 addition & 1 deletion MDANSE/Doc/conf_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
# The full version, including alpha/beta/rc tags.
release = "1.0"

html_logo = "_static/mdanse_logo.png"
# html_logo = "_static/mdanse_logo.png"

inheritance_graph_attrs = dict(size='""')

Expand Down
2 changes: 1 addition & 1 deletion MDANSE/Doc/conf_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
# The full version, including alpha/beta/rc tags.
release = "1.0"

html_logo = "_static/mdanse_logo.png"
# html_logo = "_static/mdanse_logo.png"

inheritance_graph_attrs = dict(size='""')

Expand Down
Binary file removed MDANSE/Doc/mdanse_logo.png
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,42 @@ def parse(self):
self["bonds"].append([at1, at2])
self["bonds"] = np.array(self["bonds"], dtype=np.int32)

if re.match("^\s*Atoms\s*$", line):
if re.match("^\s*Atoms\s*$", line.split("#")[0]):
if not "#" in line:
num_of_columns = len(lines[i + 2].split())
if num_of_columns <= 5:
type_index = 1
charge_index = None
line_limit = 6
else:
type_index = 2
charge_index = 3
line_limit = 7
else:
if "charge" in line.split("#")[-1]:
type_index = 1
charge_index = 2
line_limit = 6
elif "atomic" in line.split("#")[-1]:
type_index = 1
charge_index = None
line_limit = 6
elif "full" in line.split("#")[-1]:
type_index = 2
charge_index = 3
line_limit = 7
else:
type_index = 2
charge_index = 3
line_limit = 7
if self["n_atoms"] is not None:
self["atom_types"] = self["n_atoms"] * [0]
self["charges"] = self["n_atoms"] * [0.0]
for j in range(self["n_atoms"]):
atoks = lines[i + j + 1].split()
self["atom_types"][j] = int(atoks[2])
if len(atoks) >= 7:
self["charges"][j] = float(atoks[3])
self["atom_types"][j] = int(atoks[type_index])
if len(atoks) >= line_limit and charge_index is not None:
self["charges"][j] = float(atoks[charge_index])

if np.trace(np.abs(self["unit_cell"])) < 1e-8:
# print(f"Concatenated: {np.concatenate([x_inputs, y_inputs, z_inputs])}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
from abc import abstractmethod
import traceback

from MDANSE.Framework.AtomMapping import AtomLabel
from .InputFileConfigurator import InputFileConfigurator
Expand All @@ -35,7 +36,7 @@ def configure(self, filepath: str) -> None:
try:
self.parse()
except Exception as e:
self.error_status = "File parsing error"
self.error_status = f"File parsing error {e}: {traceback.format_exc()}"

@abstractmethod
def parse(self) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,13 @@ def configure(self, value):
elements = []
masses = []
names = []
group_indices = []
for i, v in enumerate(groups.values()):
names.append("group_%d" % i)
elements.append(v["elements"])
indexes.append(v["indexes"])
masses.append(v["masses"])
group_indices.append(i)

atomSelectionConfig["indexes"] = indexes
atomSelectionConfig["elements"] = elements
Expand All @@ -145,6 +147,7 @@ def configure(self, value):
atomSelectionConfig["unique_names"] = sorted(set(atomSelectionConfig["names"]))

self["level"] = value
self["group_indices"] = group_indices

@staticmethod
def find_parent(atom, level):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#

import numpy as np

from MDANSE.Framework.Configurators.IConfigurator import IConfigurator
from MDANSE.Framework.Projectors.IProjector import IProjector
Expand Down Expand Up @@ -62,13 +63,26 @@ def configure(self, value):
self.error_status = f"the projector {mode} is unknown"
return
else:
if mode == "NullProjector":
self.error_status = "OK"
return
try:
self["projector"].set_axis(axis)
vector = [float(x) for x in axis]
except ValueError:
self.error_status = f"Could not convert {axis} to numbers"
return
else:
if np.allclose(vector, 0):
self.error_status = f"Vector of 0 length does not define projection"
return
try:
self["projector"].set_axis(vector)
except ProjectorError:
self.error_status = f"Axis {axis} is wrong for this projector"
self.error_status = f"Axis {vector} is wrong for this projector"
return
else:
self["axis"] = self["projector"].axis

self.error_status = "OK"

def get_information(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,14 @@ def configure(self, value):
return

try:
generator.generate()
generator_success = generator.generate()
except:
self.error_status = "Q Vector parameters were parsed correctly, but caused an error. Invalid values?"
return
else:
if not generator_success:
self.error_status = "Q Vector parameters were parsed correctly, but caused an error. Invalid values?"
return

if not "q_vectors" in generator.configuration:
self.error_status = "Wrong inputs for q-vector generation. At the moment there are no valid Q points."
Expand Down
43 changes: 37 additions & 6 deletions MDANSE/Src/MDANSE/Framework/Converters/LAMMPS.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,18 @@ def parse_first_step(self, aliases, config):
elif line.startswith("ITEM: ATOMS"):
keywords = line.split()[2:]

self._id = keywords.index("id")
self._type = keywords.index("type")
if "id" in keywords:
self._id = keywords.index("id")
else:
self._id = None
if "type" in keywords:
self._type = keywords.index("type")
else:
self._type = None
if "element" in keywords:
self._element = keywords.index("element")
else:
self._element = None
try:
self._charge = keywords.index("q")
except ValueError:
Expand Down Expand Up @@ -194,12 +204,28 @@ def parse_first_step(self, aliases, config):
self._itemsPosition["ATOMS"] = [comp + 1, comp + self._nAtoms + 1]
for i in range(self._nAtoms):
temp = self._file.readline().split()
idx = int(temp[self._id]) - 1
ty = int(temp[self._type]) - 1
if self._id is not None:
idx = int(temp[self._id]) - 1
else:
idx = int(i)
if self._type is not None:
ty = int(temp[self._type]) - 1
else:
try:
ty = int(config["atom_types"][i]) - 1
except IndexError:
LOG.error(
f"Failed to find index [{i}] in list of len {len(config['atom_types'])}"
)
label = str(config["elements"][ty][0])
mass = str(config["elements"][ty][1])
name = "{:s}_{:d}".format(str(config["elements"][ty][0]), idx)
self._rankToName[int(temp[0]) - 1] = name
try:
temp_index = int(temp[0])
except ValueError:
self._rankToName[i] = name
else:
self._rankToName[temp_index - 1] = name
g.add_node(idx, label=label, mass=mass, atomName=name)

if config["n_bonds"] is not None:
Expand Down Expand Up @@ -335,7 +361,12 @@ def run_step(self, index):
range(self._itemsPosition["ATOMS"][0], self._itemsPosition["ATOMS"][1])
):
temp = self._file.readline().split()
idx = self._nameToIndex[self._rankToName[int(temp[0]) - 1]]
try:
temp_index = int(temp[0])
except ValueError:
idx = i
else:
idx = self._nameToIndex[self._rankToName[temp_index - 1]]
coords[idx, :] = np.array(
[temp[self._x], temp[self._y], temp[self._z]], dtype=np.float64
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def put_into_dict(name, obj):
try:
string = obj[:][0].decode()
except:
LOG.warning(f"Decode failed for {name}: {obj}")
LOG.debug(f"Decode failed for {name}: {obj}")
else:
try:
meta_dict[name] = json_decoder.decode(string)
Expand Down
37 changes: 25 additions & 12 deletions MDANSE/Src/MDANSE/Framework/Jobs/AverageStructure.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,26 @@ def initialize(self):

self._ase_atoms = Atoms()

trajectory = self.configuration["trajectory"]["instance"]

frame_range = range(
self.configuration["frames"]["first"],
self.configuration["frames"]["last"] + 1,
self.configuration["frames"]["step"],
)

try:
unit_cells = [
trajectory.unit_cell(frame)._unit_cell for frame in frame_range
]
except:
raise ValueError(
"Unit cell needs to be defined for the AverageStructure analysis. "
"You can add a unit cell using TrajectoryEditor."
)
else:
self._unit_cells = unit_cells

def run_step(self, index):
"""
Runs a single step of the job.
Expand Down Expand Up @@ -135,7 +155,10 @@ def combine(self, index, x):
# The symbol of the atom.
element = self.configuration["atom_selection"]["names"][index]

the_atom = Atom(element, x)
try:
the_atom = Atom(element, x)
except KeyError:
the_atom = Atom(str(element).strip("0123456789"), x)

self._ase_atoms.append(the_atom)

Expand All @@ -152,17 +175,7 @@ def finalize(self):
self.configuration["frames"]["step"],
)

try:
unit_cells = [
trajectory.unit_cell(frame)._unit_cell for frame in frame_range
]
except:
raise ValueError(
"Unit cell needs to be defined for the AverageStructure analysis. "
"You can add a unit cell using TrajectoryEditor."
)

average_unit_cell = np.mean(unit_cells, axis=0) * self._conversion_factor
average_unit_cell = np.mean(self._unit_cells, axis=0) * self._conversion_factor

self._ase_atoms.set_cell(average_unit_cell)

Expand Down
3 changes: 2 additions & 1 deletion MDANSE/Src/MDANSE/Framework/Jobs/DensityProfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def run_step(self, index):
conf = self.configuration["trajectory"]["instance"].configuration(frame_index)

box_coords = conf.to_box_coordinates()
box_coords = box_coords - np.floor(box_coords)

axis_index = self.configuration["axis"]["index"]
axis = conf.unit_cell.direct[axis_index, :]
Expand All @@ -146,7 +147,7 @@ def run_step(self, index):

for k, v in self._indexes_per_element.items():
h = np.histogram(
box_coords[v, axis_index], bins=self._n_bins, range=[-0.5, 0.5]
box_coords[v, axis_index], bins=self._n_bins, range=[0.0, 1.0]
)
dp_per_frame[k] = h[0]

Expand Down
Loading

0 comments on commit 9b82b6d

Please sign in to comment.