Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality for finding and reading data #78

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions mumax3c/_output_collecting_util/mumax3drive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import pathlib

import micromagneticdata as mdata
import ubermagutil as uu


@uu.inherit_docs
class Mumax3Drive(mdata.Drive):
"""Drive class for Mumax3Drives.

This class provides utility for the analysis of individual mumax3 drives. It should
not be created explicitly. Instead, use ``micromagneticdata.Drive`` which
automatically creates a ``drive`` object of the correct sub-type.

Parameters
----------
name : str

System's name.

number : int

Drive number.

dirname : str, optional

Directory in which system's data is saved. Defults to ``'./'``.

x : str, optional

Independent variable column name. Defaults to ``None`` and depending on
the driver used, one is found automatically.

use_cache : bool, optional

If ``True`` the Drive object will read tabular data and the names and number of
magnetisation files only once. Note: this prevents Drive to detect new data when
looking at the output of a running simulation. If set to ``False`` the data is
read every time the user accesses it. Defaults to ``False``.

Raises
------
IOError

If the drive directory cannot be found.

Examples
--------
1. Getting drive object.

>>> import os
>>> import micromagneticdata as md
...
>>> dirname = dirname=os.path.join(os.path.dirname(__file__),
... 'tests', 'test_sample')
>>> drive = md.Drive(name='system_name', number=1, dirname=dirname)
>>> drive
Mumax3Drive(...)

"""

def __init__(self, name, number, dirname="./", x=None, use_cache=False, **kwargs):
self._mumax_output_path = pathlib.Path(
f"{dirname}/{name}/drive-{number}/{name}.out"
) # required to initialise self.x in super
if not self._mumax_output_path.exists():
raise IOError(
f"Output directory {self._mumax_output_path!r} does not exist."
)

super().__init__(name, number, dirname, x, use_cache, **kwargs)

@mdata.AbstractDrive.x.setter
def x(self, value):
if value is None:
# self.info["driver"] in ["TimeDriver", "RelaxDriver", "MinDriver"]:
self._x = "t"
else:
# self.table reads self.x so self._x has to be defined first
if hasattr(self, "_x"):
# store old value to reset in case value is invalid
_x = self._x
self._x = value
if value not in self.table.data.columns:
self._x = _x
raise ValueError(f"Column {value=} does not exist in data.")

@property
def _table_path(self):
return self._mumax_output_path / "table.txt"

@property
def _step_file_glob(self):
return self._mumax_output_path.glob("*.ovf")

@property
def calculator_script(self):
with (self.drive_path / f"{self.name}.mx3").open() as f:
return f.read()

def __repr__(self):
"""Representation string.

Returns
-------
str

Representation string.

Examples
--------
1. Representation string.

>>> import os
>>> import micromagneticdata as md
...
>>> dirname = dirname=os.path.join(os.path.dirname(__file__),
... 'tests', 'test_sample')
>>> drive = md.Drive(name='system_name', number=1, dirname=dirname)
>>> drive
Mumax3Drive(name='system_name', number=1, dirname='...test_sample', x='t')

"""
return (
f"Mumax3Drive(name='{self.name}', number={self.number}, "
f"dirname='{self.dirname}', x='{self.x}')"
)
122 changes: 122 additions & 0 deletions mumax3c/_output_collecting_util/read_table.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import re

import pandas as pd
import ubermagtable


def table_from_file(filename, /, x=None, rename=True):
"""Convert a mumax3 ``.txt`` scalar data file into a ``ubermagtable.Table``.

Parameters
----------
filename : str

mumax3 ``.txt`` file.

x : str, optional

Independent variable name. Defaults to ``None``.

rename : bool, optional

If ``rename=True``, the column names are renamed with their shorter
versions. Defaults to ``True``.

Returns
-------
ubermagtable.Table

Table object.

TODO: update example
Examples
--------
1. Defining ``ubermagtable.Table`` by reading an OOMMF ``.odt`` file.

>>> import os
>>> import ubermagtable as ut
...
>>> odtfile = os.path.join(os.path.dirname(__file__),
... 'tests', 'test_sample',
... 'oommf-hysteresis1.odt')
>>> table = ut.Table.fromfile(odtfile, x='B_hysteresis')

2. Defining ``ubermagtable.Table`` by reading a mumax3 ``.txt`` file.

>>> odtfile = os.path.join(os.path.dirname(__file__),
... 'tests', 'test_sample', 'mumax3-file1.txt')
>>> table = ut.Table.fromfile(odtfile, x='t')

"""
quantities = _read_header(filename, rename=rename)
data = pd.read_csv(
filename,
sep=r"\s+",
comment="#",
header=None,
names=list(quantities.keys()),
)
return ubermagtable.Table(data=data, units=quantities, x=x)


def _read_header(filename, rename=True):
"""Extract quantities for individual columns from a table file.

This method extracts both column names and units and returns a dictionary,
where keys are column names and values are the units.

Parameters
----------
filename : str

OOMMF ``.odt`` or mumax3 ``.txt`` file.

rename : bool

If ``rename=True``, the column names are renamed with their shorter
versions. Defaults to ``True``.

Returns
-------
dict

Dictionary of column names and units.
"""

with open(filename) as f:
header_line_1 = f.readline()

header_line_1 = header_line_1[len("# ") :].rstrip().split("\t")
# COLUMN NAMES
cols = [elem.split()[0] for elem in header_line_1]
# UNITS
units = [re.sub(r"[()]", "", elem.split()[1]) for elem in header_line_1]

if rename:
cols = [_rename_column(col, _MUMAX3_DICT) for col in cols]

return dict(zip(cols, units))


def _rename_column(name, cols_dict):
"""Rename columns to get shorter names without spaces.

Renaming is based on _MUMAX3_DICT.
"""
return cols_dict.get(name, name)


# The mumax3 columns are renamed according to this dictionary.
_MUMAX3_DICT = {
"t": "t",
"mx": "mx",
"my": "my",
"mz": "mz",
"E_total": "E",
"E_exch": "E_totalexchange",
"E_demag": "E_demag",
"E_Zeeman": "E_zeeman",
"E_anis": "E_totalanisotropy",
"dt": "dt",
"maxTorque": "maxtorque",
}
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ homepage = "https://ubermag.github.io"
documentation = "https://ubermag.github.io/documentation/mumax3c"
repository = "https://github.com/ubermag/mumax3c"


[project.entry-points."micromagneticdata.output_collecting.Drive"]
mumax3c = "mumax3c._output_collecting_util.mumax3drive:Mumax3Drive"
[project.entry-points."micromagneticdata.output_collecting.read_table"]
mumax3c = "mumax3c._output_collecting_util.read_table:table_from_file"

[tool.black]
experimental-string-processing = true
Expand Down
Loading