diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 496f2d3..34799a4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,37 +1,23 @@ -default_language_version: - python: python3.8 +exclude: 'dev' repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-merge-conflict # checks for files that contain merge conflict strings - id: check-toml # checks toml files for parseable syntax - id: debug-statements # checks for debugger imports and py37+ `breakpoint()` calls in python source -# - id: trailing-whitespace # needs more checks -# args: [--markdown-linebreak-ext=md] -# exclude: 'mumax3c/tests/test_sample/.*' -- repo: https://github.com/pycqa/isort - rev: 5.12.0 +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.4 hooks: - - id: isort - -- repo: https://github.com/nbQA-dev/nbQA - rev: 1.7.0 - hooks: - - id: nbqa-isort # isort inside Jupyter notebooks - -- repo: https://github.com/PyCQA/flake8 - rev: 6.1.0 - hooks: - - id: flake8 - additional_dependencies: [flake8-rst-docstrings] #, flake8-docstrings] - -- repo: https://github.com/psf/black - rev: 23.9.1 - hooks: - - id: black-jupyter + # Run the linter. + - id: ruff + types_or: [python, pyi, jupyter] + args: [--fix, --exit-non-zero-on-fix] + # Run the formatter. + - id: ruff-format + types_or: [python, pyi, jupyter] # - repo: https://github.com/codespell-project/codespell # rev: v2.1.0 diff --git a/docs/tutorial-standard-problem3.ipynb b/docs/tutorial-standard-problem3.ipynb index c9839b4..1150b73 100644 --- a/docs/tutorial-standard-problem3.ipynb +++ b/docs/tutorial-standard-problem3.ipynb @@ -56,7 +56,7 @@ "\n", "# Function for initiaising the flower state.\n", "def m_init_flower(pos):\n", - " x, y, z = pos[0] / 1e-9, pos[1] / 1e-9, pos[2] / 1e-9\n", + " _x, y, z = pos[0] / 1e-9, pos[1] / 1e-9, pos[2] / 1e-9\n", " mx = 0\n", " my = 2 * z - 1\n", " mz = -2 * y + 1\n", @@ -69,7 +69,7 @@ "\n", "# Function for initialising the vortex state.\n", "def m_init_vortex(pos):\n", - " x, y, z = pos[0] / 1e-9, pos[1] / 1e-9, pos[2] / 1e-9\n", + " x, _y, _z = pos[0] / 1e-9, pos[1] / 1e-9, pos[2] / 1e-9\n", " mx = 0\n", " my = np.sin(np.pi / 2 * (x - 0.5))\n", " mz = np.cos(np.pi / 2 * (x - 0.5))\n", @@ -98,7 +98,7 @@ "outputs": [], "source": [ "def minimise_system_energy(L, m_init):\n", - " print(\"L={:9}, {} \".format(L, m_init.__name__), end=\"\")\n", + " print(f\"L={L:9}, {m_init.__name__} \", end=\"\")\n", " N = 16 # discretisation in one dimension\n", " cubesize = 100e-9 # cube edge length (m)\n", " cellsize = cubesize / N # discretisation in all three dimensions.\n", @@ -116,9 +116,9 @@ " p2 = (cubesize, cubesize, cubesize) # Maximum sample coordinate.\n", " cell = (cellsize, cellsize, cellsize) # Discretisation.\n", " mesh = df.Mesh(\n", - " p1=(0, 0, 0),\n", - " p2=(cubesize, cubesize, cubesize),\n", - " cell=(cellsize, cellsize, cellsize),\n", + " p1=p1,\n", + " p2=p2,\n", + " cell=cell,\n", " ) # Create a mesh object.\n", "\n", " system = mm.System(name=\"stdprob3\")\n", @@ -274,6 +274,8 @@ } ], "source": [ + "import matplotlib.pyplot as plt\n", + "\n", "L_array = np.linspace(8, 9, 5) # values of L for which the system is relaxed.\n", "\n", "vortex_energies = []\n", @@ -287,8 +289,6 @@ " flower_energies.append(flower.table.data.tail(1)[\"E\"][0])\n", "\n", "# Plot the energy dependences.\n", - "import matplotlib.pyplot as plt\n", - "\n", "plt.plot(L_array, vortex_energies, \"o-\", label=\"vortex\")\n", "plt.plot(L_array, flower_energies, \"o-\", label=\"flower\")\n", "plt.xlabel(\"L (lex)\")\n", diff --git a/docs/tutorial-standard-problem4.ipynb b/docs/tutorial-standard-problem4.ipynb index 5363e65..a46b96b 100644 --- a/docs/tutorial-standard-problem4.ipynb +++ b/docs/tutorial-standard-problem4.ipynb @@ -416,12 +416,10 @@ } ], "source": [ - "print(\"The average magnetisation is {}.\".format(system.m.mean()))\n", + "print(f\"The average magnetisation is {system.m.mean()}.\")\n", "\n", "print(\n", - " \"The magnetisation at the mesh centre {}\\nis {}.\".format(\n", - " system.m.mesh.region.center, system.m(system.m.mesh.region.center)\n", - " )\n", + " f\"The magnetisation at the mesh centre {system.m.mesh.region.center}\\nis {system.m(system.m.mesh.region.center)}.\"\n", ")" ] }, @@ -774,7 +772,7 @@ -580.7774351392527, 44.368495675080865, 249.74999939091504, - 62.249999986961484, + 62.24999998696149, 1.5, 0, 0, diff --git a/docs/tutorial-standard-problem5.ipynb b/docs/tutorial-standard-problem5.ipynb index 37fbb3e..ba90a26 100644 --- a/docs/tutorial-standard-problem5.ipynb +++ b/docs/tutorial-standard-problem5.ipynb @@ -185,7 +185,7 @@ ], "source": [ "def m_vortex(pos):\n", - " x, y, z = pos[0] / 1e-9 - 50, pos[1] / 1e-9 - 50, pos[2] / 1e-9\n", + " x, y, _z = pos[0] / 1e-9 - 50, pos[1] / 1e-9 - 50, pos[2] / 1e-9\n", "\n", " return (-y, x, 10)\n", "\n", diff --git a/mumax3c/__init__.py b/mumax3c/__init__.py index 0765f02..a184eca 100644 --- a/mumax3c/__init__.py +++ b/mumax3c/__init__.py @@ -1,4 +1,5 @@ """Mumax3 calculator.""" + import importlib.metadata import pytest @@ -7,8 +8,10 @@ import mumax3c.scripts # from .compute import compute # compute is not yet supported -from .delete import delete -from .drivers import MinDriver, RelaxDriver, TimeDriver +from .delete import delete as delete +from .drivers import MinDriver as MinDriver +from .drivers import RelaxDriver as RelaxDriver +from .drivers import TimeDriver as TimeDriver runner = mumax3c.mumax3.Runner() """Controls the default runner. diff --git a/mumax3c/drivers/__init__.py b/mumax3c/drivers/__init__.py index 602ee8b..e75a854 100644 --- a/mumax3c/drivers/__init__.py +++ b/mumax3c/drivers/__init__.py @@ -1,4 +1,4 @@ -from .driver import Driver -from .mindriver import MinDriver -from .relaxdriver import RelaxDriver -from .timedriver import TimeDriver +from .driver import Driver as Driver +from .mindriver import MinDriver as MinDriver +from .relaxdriver import RelaxDriver as RelaxDriver +from .timedriver import TimeDriver as TimeDriver diff --git a/mumax3c/drivers/driver.py b/mumax3c/drivers/driver.py index daf608c..4385275 100644 --- a/mumax3c/drivers/driver.py +++ b/mumax3c/drivers/driver.py @@ -127,7 +127,7 @@ def write_mx3(self, system, dirname=".", ovf_format="bin8", abspath=True, **kwar ovf_format=ovf_format, **kwargs, ) - with open(self._mx3filename(system), "wt", encoding="utf-8") as mx3file: + with open(self._mx3filename(system), "w", encoding="utf-8") as mx3file: mx3file.write(mx3) # Generate and save json info file for a drive (not compute). diff --git a/mumax3c/mumax3/__init__.py b/mumax3c/mumax3/__init__.py index ff7db10..193eaea 100644 --- a/mumax3c/mumax3/__init__.py +++ b/mumax3c/mumax3/__init__.py @@ -1 +1,4 @@ -from .mumax3 import ExeMumax3Runner, Mumax3Runner, Runner, overhead +from .mumax3 import ExeMumax3Runner as ExeMumax3Runner +from .mumax3 import Mumax3Runner as Mumax3Runner +from .mumax3 import Runner as Runner +from .mumax3 import overhead as overhead diff --git a/mumax3c/mumax3/mumax3.py b/mumax3c/mumax3/mumax3.py index 68e8b42..e487884 100644 --- a/mumax3c/mumax3/mumax3.py +++ b/mumax3c/mumax3/mumax3.py @@ -56,7 +56,7 @@ def status(self): td.drive(system, t=1e-12, n=1, runner=self) print("mumax3 found and running.") return 0 - except (EnvironmentError, RuntimeError): + except (OSError, RuntimeError): print("Cannot find mumax3.") return 1 @@ -210,7 +210,7 @@ def autoselect_runner(self): " Ubermag. Make sure it is properly installed and can be found on the" " command line." ) - raise EnvironmentError(msg) + raise OSError(msg) def overhead(): @@ -235,22 +235,21 @@ def overhead(): True """ - with tempfile.TemporaryDirectory() as workingdir: - with uu.changedir(workingdir): - # Running mumax3 through mumax3c. - system = mm.examples.macrospin() - td = mc.TimeDriver() - mumax3c_start = time.time() - td.drive(system, t=1e-12, n=1) - mumax3c_stop = time.time() - mumax3c_time = mumax3c_stop - mumax3c_start - - # Running mumax3 directly. - mumax3_runner = mc.runner.runner - mx3path = pathlib.Path(f"{system.name}/drive-0/macrospin.mx3").resolve() - mumax3_start = time.time() - mumax3_runner.call(str(mx3path)) - mumax3_stop = time.time() - mumax3_time = mumax3_stop - mumax3_start + with tempfile.TemporaryDirectory() as workingdir, uu.changedir(workingdir): + # Running mumax3 through mumax3c. + system = mm.examples.macrospin() + td = mc.TimeDriver() + mumax3c_start = time.time() + td.drive(system, t=1e-12, n=1) + mumax3c_stop = time.time() + mumax3c_time = mumax3c_stop - mumax3c_start + + # Running mumax3 directly. + mumax3_runner = mc.runner.runner + mx3path = pathlib.Path(f"{system.name}/drive-0/macrospin.mx3").resolve() + mumax3_start = time.time() + mumax3_runner.call(str(mx3path)) + mumax3_stop = time.time() + mumax3_time = mumax3_stop - mumax3_start return mumax3c_time - mumax3_time diff --git a/mumax3c/scripts/__init__.py b/mumax3c/scripts/__init__.py index 17ca2be..006be9a 100644 --- a/mumax3c/scripts/__init__.py +++ b/mumax3c/scripts/__init__.py @@ -1,6 +1,7 @@ -from .driver import driver_script -from .energy import energy_script -from .magnetisation import magnetisation_script -from .mesh import mesh_script -from .system import system_script -from .util import mumax3_regions, set_parameter +from .driver import driver_script as driver_script +from .energy import energy_script as energy_script +from .magnetisation import magnetisation_script as magnetisation_script +from .mesh import mesh_script as mesh_script +from .system import system_script as system_script +from .util import mumax3_regions as mumax3_regions +from .util import set_parameter as set_parameter diff --git a/mumax3c/scripts/util.py b/mumax3c/scripts/util.py index 9d905b4..949352e 100644 --- a/mumax3c/scripts/util.py +++ b/mumax3c/scripts/util.py @@ -55,9 +55,9 @@ def mumax3_regions(system, ovf_format="bin4", abspath=True): continue unique_index += 1 mx3 += f"Msat.setregion({unique_index}, {ms})\n" - region_indices[ - (sr_indices == sr_index) & np.isclose(Ms_array, ms) - ] = unique_index + region_indices[(sr_indices == sr_index) & np.isclose(Ms_array, ms)] = ( + unique_index + ) region_relator[sr_name].append(unique_index) if unique_index > max_index: diff --git a/mumax3c/tests/conftest.py b/mumax3c/tests/conftest.py index c85006c..77357b6 100644 --- a/mumax3c/tests/conftest.py +++ b/mumax3c/tests/conftest.py @@ -8,6 +8,7 @@ "TestExchange.test_field", "test_simple_hysteresis_loop", "test_stepped_hysteresis_loop", + "test_hysteresis_check_for_energy", "TestMinDriver.test_evolver_nodriver", "TestMinDriver.test_evolver_driver", "TestMinDriver.test_wrong_evolver", diff --git a/pyproject.toml b/pyproject.toml index 803e0a7..345f057 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ authors = [ ] classifiers = [ - "Development Status :: 4 - Beta", + "Development Status :: 5 - Production/Stable", "Intended Audience :: Education", "Intended Audience :: Developers", "Intended Audience :: Science/Research", @@ -66,15 +66,49 @@ repository = "https://github.com/ubermag/mumax3c" -[tool.black] -experimental-string-processing = true [tool.coverage.run] omit = ["mumax3c/tests/*"] -[tool.isort] -profile = "black" -skip_gitignore = true # ignores files listed in .gitignore +[tool.ruff.lint] +ignore-init-module-imports = true # do not remove unused imports in __init__ and warn instead +select = [ + "B", # flake8-bugbear + "E", # pycodestyle + "F", # Pyflakes + "I", # isort + "SIM", # flake8-simplify + "UP", # pyupgrade +] +ignore = [ + # conflict with other rules + "D203", # one-blank-line-before-class (conflicts with D204) + "D212", # multi-line-summary-first-line (conflicts with D213) + # conflict with formatter + "D206", # indent-with-spaces + "D300", # triple-single-quotes + "E111", # indentation-with-invalid-multiple + "E114", # indentation-with-invalid-multiple-comment + "E117", # over-indented + # conflict with Python 3.6 compatibility + "UP022", # replace-stdout-stderr +] + +[tool.ruff.lint.isort] +known-local-folder = ["mumax3c"] + +[tool.ruff.lint.per-file-ignores] +"*.ipynb" = [ + "B018", # "Found useless expression. Either assign it to a variable or remove it."; false positives when using implicit __repr__ in the notebook + "E501", # line too long + "F811", # 'redefined-while-unused'; many false positives in notebooks because ipywidgets decorated functions are not recognised +] + +[tool.pytest.ini_options] +filterwarnings = [ + "error", + "ignore:((.|\n)*)Sentinel is not a public part of the traitlets API((.|\n)*)", # dependency of k3d +] [tool.setuptools.packages.find] include = ["mumax3c*"] diff --git a/setup.cfg b/setup.cfg index 9613901..a73006a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,30 +1,2 @@ -[flake8] -exclude = - .git, - __pycache__, - build, - dev, - dist, - setup.py -# black has a longer default line length -max-line-length = 88 -# D107: missing docstring in __init__ -# E203: withespace before ':', required for black -# RST210: Inline strong start-string without end-string. # complains about "**kwargs" in docstrings -extend-ignore = D107,RST210,E203 -per-file-ignores = - # imported but unused - __init__.py: F401 - # ignore missing docstrings in tests - test_*.py: D100,D101,D102,D103 -docstring-convention: numpy -# flake8-rst-docstrings: -rst-roles = - py:class, - py:func, -rst-directives = - seealso, - plot, - [codespell] skip = .*,build/*,dev/*,dist/* diff --git a/tasks.py b/tasks.py index 8bf080b..6cb0ed3 100644 --- a/tasks.py +++ b/tasks.py @@ -1,4 +1,5 @@ """Tasks to release the package.""" + import os import shutil