Skip to content

Commit 9b82b6d

Browse files
Merge pull request #573 from ISISNeutronMuon/beta-release-2024
Beta release 2024
2 parents f11a6b4 + dd68dd4 commit 9b82b6d

38 files changed

+261
-86
lines changed

CHANGELOG

+12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
version 2.0.0b1 - 2024-10-15
2+
--------------------------
3+
* ADDED van Hove functions (self function and distinct function)
4+
* FIXED Eccentricity analysis to give correct results
5+
* ADDED Infrared analysis (only for molecules at the moment)
6+
* FIXED MolecularTrace analysis data axes to make data plottable
7+
* CHANGED the plotting interface to allow overplotting curves
8+
* ADDED instrument profiles for storing user parameters
9+
* ADDED logging in the code and a logger tab in the GUI
10+
* ADDED charge information to the stored trajectory data
11+
* ADDED support for H5MD trajectories
12+
113
version 2.0.0 - 2024-02-15
214
--------------------------
315
* CHANGED programming language to Python 3

Doc/conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
# project = 'MDANSE'
4242
# copyright = '2015-2022, Eric Pellegrini'
4343
author = 'Eric Pellegrini'
44-
release = '2.0.0a1'
44+
release = '2.0.0b1'
4545
version = '2.0'
4646

4747
# -- General configuration ---------------------------------------------------

Doc/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Welcome to MDANSE's documentation!
99
.. note::
1010
This is the documentation of the MDANSE 2.0 release.
1111
The documentation, just like the code itself, is still under development.
12+
MDANSE 2 has currently (October 2024) just reached the first beta release.
1213

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

Doc/pages/H_start.rst

+23-18
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ Use `pip` to install the MDANSE package from the specified GitHub repository:
5757
5858
pip install MDANSE
5959
60+
The MDANSE package contains all the code needed to perform trajectory conversion
61+
and analysis using MDANSE, but none of the visualisation tools.
62+
6063
Install MDANSE_GUI Package
6164
~~~~~~~~~~~~~~~~~~~~~~~~~~~
6265

@@ -66,14 +69,9 @@ Similarly, install the MDANSE_GUI package using `pip`:
6669
6770
pip install MDANSE_GUI
6871
69-
Run MDANSE
70-
~~~~~~~~~~
71-
72-
You can now start using MDANSE by running the following command:
73-
74-
.. code-block:: bash
75-
76-
mdanse_gui
72+
From now on, the `mdanse_gui` command will be available to start
73+
the graphical interface of MDANSE, which makes it easier to create
74+
valid inputs for different analysis types.
7775

7876
Run MDANSE
7977
~~~~~~~~~~
@@ -84,13 +82,20 @@ You can now start using MDANSE by running the following command:
8482
8583
mdanse_gui
8684
87-
This will launch the MDANSE Graphical User Interface (GUI), and you can start using MDANSE for your
88-
analysis.
89-
90-
Note for Windows Users: On Windows, the command to run MDANSE may need to be:
91-
92-
.. code-block:: bash
93-
94-
python3 mdanse_gui
95-
96-
That's it! You have successfully installed MDANSE and are ready to use it for your data analysis needs.
85+
This will launch the MDANSE Graphical User Interface (GUI),
86+
and you can start using MDANSE for your analysis.
87+
88+
MDANSE Scripts
89+
~~~~~~~~~~~~~~
90+
91+
If you intend to run your analysis on a remote platform
92+
(e.g. a cluster), most likely you will have limited options
93+
of using the GUI there. However, you can still prepare
94+
a script using MDANSE_GUI on your own computer, save it
95+
and transfer it to the other computer to run the analysis
96+
there. You will need to change the file paths in the script,
97+
but all the other parameters should be transferable. One
98+
of the design principles of MDANSE 2 is that the scripts
99+
should not depend on any settings stored locally on
100+
a specific computer, but should instead contain all the
101+
information needed to run a specific analysis type.

MDANSE/Doc/_static/mdanse_logo.png

-65 KB
Binary file not shown.

MDANSE/Doc/conf_api.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
# The full version, including alpha/beta/rc tags.
7171
release = "1.0"
7272

73-
html_logo = "_static/mdanse_logo.png"
73+
# html_logo = "_static/mdanse_logo.png"
7474

7575
inheritance_graph_attrs = dict(size='""')
7676

MDANSE/Doc/conf_help.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
# The full version, including alpha/beta/rc tags.
6767
release = "1.0"
6868

69-
html_logo = "_static/mdanse_logo.png"
69+
# html_logo = "_static/mdanse_logo.png"
7070

7171
inheritance_graph_attrs = dict(size='""')
7272

MDANSE/Doc/mdanse_logo.png

-65 KB
Binary file not shown.

MDANSE/Src/MDANSE/Framework/Configurators/ConfigFileConfigurator.py

+31-4
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,42 @@ def parse(self):
140140
self["bonds"].append([at1, at2])
141141
self["bonds"] = np.array(self["bonds"], dtype=np.int32)
142142

143-
if re.match("^\s*Atoms\s*$", line):
143+
if re.match("^\s*Atoms\s*$", line.split("#")[0]):
144+
if not "#" in line:
145+
num_of_columns = len(lines[i + 2].split())
146+
if num_of_columns <= 5:
147+
type_index = 1
148+
charge_index = None
149+
line_limit = 6
150+
else:
151+
type_index = 2
152+
charge_index = 3
153+
line_limit = 7
154+
else:
155+
if "charge" in line.split("#")[-1]:
156+
type_index = 1
157+
charge_index = 2
158+
line_limit = 6
159+
elif "atomic" in line.split("#")[-1]:
160+
type_index = 1
161+
charge_index = None
162+
line_limit = 6
163+
elif "full" in line.split("#")[-1]:
164+
type_index = 2
165+
charge_index = 3
166+
line_limit = 7
167+
else:
168+
type_index = 2
169+
charge_index = 3
170+
line_limit = 7
144171
if self["n_atoms"] is not None:
145172
self["atom_types"] = self["n_atoms"] * [0]
146173
self["charges"] = self["n_atoms"] * [0.0]
147174
for j in range(self["n_atoms"]):
148175
atoks = lines[i + j + 1].split()
149-
self["atom_types"][j] = int(atoks[2])
150-
if len(atoks) >= 7:
151-
self["charges"][j] = float(atoks[3])
176+
self["atom_types"][j] = int(atoks[type_index])
177+
if len(atoks) >= line_limit and charge_index is not None:
178+
self["charges"][j] = float(atoks[charge_index])
152179

153180
if np.trace(np.abs(self["unit_cell"])) < 1e-8:
154181
# print(f"Concatenated: {np.concatenate([x_inputs, y_inputs, z_inputs])}")

MDANSE/Src/MDANSE/Framework/Configurators/FileWithAtomDataConfigurator.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1515
#
1616
from abc import abstractmethod
17+
import traceback
1718

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

4041
@abstractmethod
4142
def parse(self) -> None:

MDANSE/Src/MDANSE/Framework/Configurators/GroupingLevelConfigurator.py

+3
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,13 @@ def configure(self, value):
131131
elements = []
132132
masses = []
133133
names = []
134+
group_indices = []
134135
for i, v in enumerate(groups.values()):
135136
names.append("group_%d" % i)
136137
elements.append(v["elements"])
137138
indexes.append(v["indexes"])
138139
masses.append(v["masses"])
140+
group_indices.append(i)
139141

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

147149
self["level"] = value
150+
self["group_indices"] = group_indices
148151

149152
@staticmethod
150153
def find_parent(atom, level):

MDANSE/Src/MDANSE/Framework/Configurators/ProjectionConfigurator.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# along with this program. If not, see <https://www.gnu.org/licenses/>.
1515
#
1616

17+
import numpy as np
1718

1819
from MDANSE.Framework.Configurators.IConfigurator import IConfigurator
1920
from MDANSE.Framework.Projectors.IProjector import IProjector
@@ -62,13 +63,26 @@ def configure(self, value):
6263
self.error_status = f"the projector {mode} is unknown"
6364
return
6465
else:
66+
if mode == "NullProjector":
67+
self.error_status = "OK"
68+
return
6569
try:
66-
self["projector"].set_axis(axis)
70+
vector = [float(x) for x in axis]
71+
except ValueError:
72+
self.error_status = f"Could not convert {axis} to numbers"
73+
return
74+
else:
75+
if np.allclose(vector, 0):
76+
self.error_status = f"Vector of 0 length does not define projection"
77+
return
78+
try:
79+
self["projector"].set_axis(vector)
6780
except ProjectorError:
68-
self.error_status = f"Axis {axis} is wrong for this projector"
81+
self.error_status = f"Axis {vector} is wrong for this projector"
6982
return
7083
else:
7184
self["axis"] = self["projector"].axis
85+
7286
self.error_status = "OK"
7387

7488
def get_information(self):

MDANSE/Src/MDANSE/Framework/Configurators/QVectorsConfigurator.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,14 @@ def configure(self, value):
7272
return
7373

7474
try:
75-
generator.generate()
75+
generator_success = generator.generate()
7676
except:
7777
self.error_status = "Q Vector parameters were parsed correctly, but caused an error. Invalid values?"
7878
return
79+
else:
80+
if not generator_success:
81+
self.error_status = "Q Vector parameters were parsed correctly, but caused an error. Invalid values?"
82+
return
7983

8084
if not "q_vectors" in generator.configuration:
8185
self.error_status = "Wrong inputs for q-vector generation. At the moment there are no valid Q points."

MDANSE/Src/MDANSE/Framework/Converters/LAMMPS.py

+37-6
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,18 @@ def parse_first_step(self, aliases, config):
159159
elif line.startswith("ITEM: ATOMS"):
160160
keywords = line.split()[2:]
161161

162-
self._id = keywords.index("id")
163-
self._type = keywords.index("type")
162+
if "id" in keywords:
163+
self._id = keywords.index("id")
164+
else:
165+
self._id = None
166+
if "type" in keywords:
167+
self._type = keywords.index("type")
168+
else:
169+
self._type = None
170+
if "element" in keywords:
171+
self._element = keywords.index("element")
172+
else:
173+
self._element = None
164174
try:
165175
self._charge = keywords.index("q")
166176
except ValueError:
@@ -194,12 +204,28 @@ def parse_first_step(self, aliases, config):
194204
self._itemsPosition["ATOMS"] = [comp + 1, comp + self._nAtoms + 1]
195205
for i in range(self._nAtoms):
196206
temp = self._file.readline().split()
197-
idx = int(temp[self._id]) - 1
198-
ty = int(temp[self._type]) - 1
207+
if self._id is not None:
208+
idx = int(temp[self._id]) - 1
209+
else:
210+
idx = int(i)
211+
if self._type is not None:
212+
ty = int(temp[self._type]) - 1
213+
else:
214+
try:
215+
ty = int(config["atom_types"][i]) - 1
216+
except IndexError:
217+
LOG.error(
218+
f"Failed to find index [{i}] in list of len {len(config['atom_types'])}"
219+
)
199220
label = str(config["elements"][ty][0])
200221
mass = str(config["elements"][ty][1])
201222
name = "{:s}_{:d}".format(str(config["elements"][ty][0]), idx)
202-
self._rankToName[int(temp[0]) - 1] = name
223+
try:
224+
temp_index = int(temp[0])
225+
except ValueError:
226+
self._rankToName[i] = name
227+
else:
228+
self._rankToName[temp_index - 1] = name
203229
g.add_node(idx, label=label, mass=mass, atomName=name)
204230

205231
if config["n_bonds"] is not None:
@@ -335,7 +361,12 @@ def run_step(self, index):
335361
range(self._itemsPosition["ATOMS"][0], self._itemsPosition["ATOMS"][1])
336362
):
337363
temp = self._file.readline().split()
338-
idx = self._nameToIndex[self._rankToName[int(temp[0]) - 1]]
364+
try:
365+
temp_index = int(temp[0])
366+
except ValueError:
367+
idx = i
368+
else:
369+
idx = self._nameToIndex[self._rankToName[temp_index - 1]]
339370
coords[idx, :] = np.array(
340371
[temp[self._x], temp[self._y], temp[self._z]], dtype=np.float64
341372
)

MDANSE/Src/MDANSE/Framework/InputData/HDFTrajectoryInputData.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def put_into_dict(name, obj):
107107
try:
108108
string = obj[:][0].decode()
109109
except:
110-
LOG.warning(f"Decode failed for {name}: {obj}")
110+
LOG.debug(f"Decode failed for {name}: {obj}")
111111
else:
112112
try:
113113
meta_dict[name] = json_decoder.decode(string)

MDANSE/Src/MDANSE/Framework/Jobs/AverageStructure.py

+25-12
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,26 @@ def initialize(self):
9797

9898
self._ase_atoms = Atoms()
9999

100+
trajectory = self.configuration["trajectory"]["instance"]
101+
102+
frame_range = range(
103+
self.configuration["frames"]["first"],
104+
self.configuration["frames"]["last"] + 1,
105+
self.configuration["frames"]["step"],
106+
)
107+
108+
try:
109+
unit_cells = [
110+
trajectory.unit_cell(frame)._unit_cell for frame in frame_range
111+
]
112+
except:
113+
raise ValueError(
114+
"Unit cell needs to be defined for the AverageStructure analysis. "
115+
"You can add a unit cell using TrajectoryEditor."
116+
)
117+
else:
118+
self._unit_cells = unit_cells
119+
100120
def run_step(self, index):
101121
"""
102122
Runs a single step of the job.
@@ -135,7 +155,10 @@ def combine(self, index, x):
135155
# The symbol of the atom.
136156
element = self.configuration["atom_selection"]["names"][index]
137157

138-
the_atom = Atom(element, x)
158+
try:
159+
the_atom = Atom(element, x)
160+
except KeyError:
161+
the_atom = Atom(str(element).strip("0123456789"), x)
139162

140163
self._ase_atoms.append(the_atom)
141164

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

155-
try:
156-
unit_cells = [
157-
trajectory.unit_cell(frame)._unit_cell for frame in frame_range
158-
]
159-
except:
160-
raise ValueError(
161-
"Unit cell needs to be defined for the AverageStructure analysis. "
162-
"You can add a unit cell using TrajectoryEditor."
163-
)
164-
165-
average_unit_cell = np.mean(unit_cells, axis=0) * self._conversion_factor
178+
average_unit_cell = np.mean(self._unit_cells, axis=0) * self._conversion_factor
166179

167180
self._ase_atoms.set_cell(average_unit_cell)
168181

MDANSE/Src/MDANSE/Framework/Jobs/DensityProfile.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ def run_step(self, index):
137137
conf = self.configuration["trajectory"]["instance"].configuration(frame_index)
138138

139139
box_coords = conf.to_box_coordinates()
140+
box_coords = box_coords - np.floor(box_coords)
140141

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

147148
for k, v in self._indexes_per_element.items():
148149
h = np.histogram(
149-
box_coords[v, axis_index], bins=self._n_bins, range=[-0.5, 0.5]
150+
box_coords[v, axis_index], bins=self._n_bins, range=[0.0, 1.0]
150151
)
151152
dp_per_frame[k] = h[0]
152153

0 commit comments

Comments
 (0)