From 8a43ba0eb32f0229021ddc5b2bade13890345846 Mon Sep 17 00:00:00 2001 From: glass-ships Date: Thu, 14 Nov 2024 15:08:31 -0500 Subject: [PATCH 01/43] Formatting changes, update imports, other minor changes --- .github/workflows/test.yml | 1 - .gitignore | 1 + Makefile | 14 ++ compareopt/slabs.py | 6 +- conftest.py | 1 + doc/_extensions/wx_directive.py | 19 +- doc/conf.py | 3 +- doc/examples/distribution/dist-example.py | 3 +- doc/genmods.py | 11 +- doc/getting_started/contributing.rst | 110 +++++++----- doc/getting_started/install.rst | 170 +++++++++++------- doc/getting_started/server.rst | 2 - doc/pylit.py | 10 +- doc/requirements.txt | 8 +- {refl1d => explore}/corrtest.py | 3 +- explore/interface.py | 8 +- {refl1d => explore}/refl_tr.py | 7 +- extra/gen_schema.py | 7 + extra/gen_schema_v2.py | 13 +- pyproject.toml | 14 +- refl1d/abeles.py | 8 +- refl1d/anstodata.py | 5 +- refl1d/cheby.py | 11 +- refl1d/errors.py | 12 +- refl1d/fasta.py | 2 - refl1d/flayer.py | 8 +- refl1d/fresnel.py | 4 +- refl1d/instrument.py | 3 - refl1d/lib/numba/clone_module.py | 1 - refl1d/lib/python/magnetic.py | 2 +- refl1d/magnetism.py | 11 +- refl1d/material.py | 35 +--- refl1d/model.py | 79 +------- refl1d/mono.py | 13 +- refl1d/names.py | 37 ++-- refl1d/oversampling.py | 3 +- refl1d/polymer.py | 10 +- refl1d/probe.py | 38 ++-- refl1d/profile.py | 8 +- refl1d/reflectivity.py | 4 +- refl1d/refllib.py | 8 +- refl1d/resolution.py | 26 ++- refl1d/staj.py | 4 +- refl1d/stajconvert.py | 2 - refl1d/support.py | 5 +- refl1d/util.py | 12 +- refl1d/view/data_view.py | 20 +-- refl1d/view/interactor.py | 5 +- refl1d/view/layer.py | 6 +- refl1d/view/layer_dialog.py | 8 +- refl1d/view/model_view.py | 24 +-- refl1d/view/polymeri.py | 7 +- refl1d/view/profilei.py | 12 +- refl1d/view/registry.py | 6 +- refl1d/webview/client/package.json | 3 +- refl1d/webview/server/api.py | 30 ++-- refl1d/webview/server/names.py | 8 +- refl1d/webview/server/profile_plot.py | 8 +- refl1d/webview/server/profile_uncertainty.py | 6 +- refl1d/webview/server/webserver.py | 15 +- release.md | 5 +- .../mystic/{bounds_test.py => test_bounds.py} | 0 .../{condition_test.py => test_condition.py} | 0 tests/mystic/{de_test.py => test_de.py} | 0 ...{fitservice_test.py => test_fitservice.py} | 0 .../{history_test.py => test_history.py} | 0 .../{parameter_test.py => test_parameter.py} | 0 tests/mystic/{stop_test.py => test_stop.py} | 0 tests/refl1d/Q_probe_oversample.py | 5 +- tests/refl1d/gepore_check.py | 8 +- tests/refl1d/surround_variation_gen.py | 2 - ...agnetic_test.py => test_align_magnetic.py} | 38 ++-- .../{anstodata_test.py => test_anstodata.py} | 3 +- .../{ncnrdata_test.py => test_ncnrdata.py} | 1 + .../{polymer_test.py => test_polymer.py} | 11 +- ...{resolution_test.py => test_resolution.py} | 8 +- .../{snsdata_test.py => test_snsdata.py} | 0 tests/refl1d/{stack_test.py => test_stack.py} | 8 +- tests/refl1d/zeeman_check.py | 10 +- ...{experiment_test.py => test_experiment.py} | 6 +- 80 files changed, 498 insertions(+), 527 deletions(-) rename {refl1d => explore}/corrtest.py (98%) rename {refl1d => explore}/refl_tr.py (98%) rename tests/mystic/{bounds_test.py => test_bounds.py} (100%) rename tests/mystic/{condition_test.py => test_condition.py} (100%) rename tests/mystic/{de_test.py => test_de.py} (100%) rename tests/mystic/{fitservice_test.py => test_fitservice.py} (100%) rename tests/mystic/{history_test.py => test_history.py} (100%) rename tests/mystic/{parameter_test.py => test_parameter.py} (100%) rename tests/mystic/{stop_test.py => test_stop.py} (100%) rename tests/refl1d/{align_magnetic_test.py => test_align_magnetic.py} (81%) rename tests/refl1d/{anstodata_test.py => test_anstodata.py} (81%) rename tests/refl1d/{ncnrdata_test.py => test_ncnrdata.py} (99%) rename tests/refl1d/{polymer_test.py => test_polymer.py} (99%) rename tests/refl1d/{resolution_test.py => test_resolution.py} (96%) rename tests/refl1d/{snsdata_test.py => test_snsdata.py} (100%) rename tests/refl1d/{stack_test.py => test_stack.py} (96%) rename tests/{experiment_test.py => test_experiment.py} (97%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b5fb2bb0..6813303a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,5 +45,4 @@ jobs: - name: Check that the docs build (linux only) if: matrix.config.doc == 1 run: | - pip install sphinx make -j 4 -C doc SPHINXOPTS="-W --keep-going" html diff --git a/.gitignore b/.gitignore index e690853d..f542cd84 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ refl1d-webview-client*.tgz /bumps-* /Refl1D.pdf /output-* +test-files/ # Build *.so diff --git a/Makefile b/Makefile index 12e73659..5b095bbf 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,20 @@ ROOTDIR = $(shell pwd) help: ## Print this help message @perl -nle'print $& if m{^[/a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-25s\033[0m %s\n", $$1, $$2}' +.PHONY: clean +clean: ## Delete some cruft from builds/testing/etc. + rm -f `find . -type f -name '*.py[co]'` + rm -rf `find . -name __pycache__ -o -name "*.egg-info"` \ + `find . -name 'output-*'` \ + .coverage build dist \ + doc/_build doc/api doc/tutorial \ + .pytest_cache \ + .ruff_cache + +.PHONY: test +test: ## Run pytest and doc tests + pytest -v + python check_examples.py --chisq .PHONY: lint lint: ## Run ruff linting diff --git a/compareopt/slabs.py b/compareopt/slabs.py index 4f95bd9c..1b19f837 100755 --- a/compareopt/slabs.py +++ b/compareopt/slabs.py @@ -1,9 +1,9 @@ -from __future__ import print_function - from random import getrandbits + +from bumps.parameter import summarize from numpy.random import uniform + from refl1d.names import * -from bumps.parameter import summarize num_layers = int(sys.argv[1]) init_file = sys.argv[2] if len(sys.argv) > 2 else "/tmp/problem" diff --git a/conftest.py b/conftest.py index 8f25f1e8..200d537d 100644 --- a/conftest.py +++ b/conftest.py @@ -1,5 +1,6 @@ collect_ignore = [ "doc/guide/toffset.py", + # "refl1d/wx_gui/", "tests/refl1d/Q_probe_oversample.py", # "tests/refl1d/snsdata_test.py", ] diff --git a/doc/_extensions/wx_directive.py b/doc/_extensions/wx_directive.py index 0cd75352..ebd4c57b 100644 --- a/doc/_extensions/wx_directive.py +++ b/doc/_extensions/wx_directive.py @@ -15,10 +15,14 @@ # Note: adapted from matplotlib.sphinxext.plot_directive by Paul Kienzle -from six.moves import StringIO - -import sys, os, glob, shutil, hashlib, imp, warnings +import imp +import os import re +import shutil +import sys +import warnings + +from six.moves import StringIO try: from hashlib import md5 @@ -34,16 +38,13 @@ from docutils.parsers.rst.directives.images import Image align = Image.align -from docutils import nodes -import sphinx - -import wx - # Matplotlib helper utilities import matplotlib.cbook as cbook import numpy import png - +import sphinx +import wx +from docutils import nodes sphinx_version = sphinx.__version__.split(".") # The split is necessary for sphinx beta versions where the string is diff --git a/doc/conf.py b/doc/conf.py index 6ce25734..d6f4195d 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -11,7 +11,8 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +import os +import sys print("python %s" % sys.executable) diff --git a/doc/examples/distribution/dist-example.py b/doc/examples/distribution/dist-example.py index 2168985f..f27e1ef5 100644 --- a/doc/examples/distribution/dist-example.py +++ b/doc/examples/distribution/dist-example.py @@ -1,4 +1,5 @@ -import sys, os +import os +import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from refl1d.names import * diff --git a/doc/genmods.py b/doc/genmods.py index 0a6658c1..cdf10bfa 100644 --- a/doc/genmods.py +++ b/doc/genmods.py @@ -19,7 +19,11 @@ MODULE_TEMPLATE is the template for each api module. """ -from __future__ import with_statement, print_function +import inspect +import sys +from os import makedirs +from os.path import abspath, dirname, exists, getmtime +from os.path import join as joinpath OPTIONS = { "absolute": False, # True if package.module in table of contents @@ -119,11 +123,6 @@ """ # ===================== Documentation generator ===================== -from os import makedirs -from os.path import exists, dirname, getmtime, join as joinpath, abspath -import inspect -import sys - def newer(file1, file2): return not exists(file1) or (getmtime(file1) < getmtime(file2)) diff --git a/doc/getting_started/contributing.rst b/doc/getting_started/contributing.rst index 30d5eabf..2d3a0d73 100755 --- a/doc/getting_started/contributing.rst +++ b/doc/getting_started/contributing.rst @@ -6,6 +6,11 @@ Contributing Changes .. contents:: :local: + +The refl1d package is a community project, and we welcome contributions from anyone. +The package is developed collaboratively on `Github `_ - if you don't have an account yet, you can sign up for free. For direct write access to the repository, it is required that your accout have `two-factor authentication enabled `_. +You may also want to configure your account to use `SSH keys `_ for authentication. + The best way to contribute to the reflectometry package is to work from a copy of the source tree in the revision control system. @@ -18,27 +23,55 @@ be downloaded from the `git page `_, or you can use an integrated development environment (IDE) such as Eclipse and PyCharm, which may have git built in. -Simple patches --------------- -If you want to make one or two tiny changes, it is easiest to clone the -project, make the changes, document and test, then send a patch. +Getting the Code +================ + +To get the code, you will need to clone the repository. If you are planning +on making only a few small changes, you can clone the repository directly, +make the changes, document and test, then send a patch (see `Simple patches <#Simple-patches>`_ below). + +If you are planning on making larger changes, you should fork the repository +on github, make the changes in your fork, then issue a pull request to the +main repository (see `Larger changes <#Larger-changes>`_ below). + +.. note:: + + If you are working on a fork, the clone line is slightly different:: + + git clone https://github.com/YourGithubAccount/refl1d + + + You will also need to keep your fork up to date + with the main repository. You can do this by adding the main repository + as a remote, fetching the changes, then merging them into your fork. + + .. code-block:: bash + + # Add the main repository as a remote + git remote add refl1d -Clone the project as follows:: + # Fetch the changes from the main repository + git fetch refl1d - git clone https://github.com/reflectometry/refl1d.git + # Merge the changes into your fork + git merge refl1d/master -You will need bumps and periodictable to run. If you are fixing bugs in the -scattering length density calculator or the fitting engine, you will want to -clone the repositories as sister directories to the refl1d source tree:: + # Push the changes to your fork + git push - git clone https://github.com/bumps/bumps.git - git clone https://github.com/pkienzle/periodictable.git -If you are only working with the refl1d modeling code, then you can install -bumps and periodictable using pip:: +Once you have the code, you will need to install Refl1D, including its dependencies. +You can do this by following the instructions in the `Installation guide `_. - pip install periodictable bumps +.. simple-patches: + +Simple patches +-------------- + +If you want to make one or two tiny changes, it is easiest to clone the +repository, make the changes, then send a patch. This is the simplest way +to contribute to the project. To run the package from the source tree use the following:: @@ -55,13 +88,13 @@ As you make changes to the package, you can see what you have done using git:: git diff Please update the documentation and add tests for your changes. We use -doctests on all of our examples that we know our documentation is correct. -More thorough tests are found in test directory. With the nosetest package, -you can run the tests using:: - - python tests.py +doctests on all of our examples so that we know our documentation is correct. +More thorough tests are found in test directory. You can run these tests via pytest, +or via the convenience Makefile target:: -Nose is available on linux form apt-get + pytest + # or + make test When all the tests run, create a patch and send it to paul.kienzle@nist.gov:: @@ -73,14 +106,9 @@ Larger changes For a larger set of changes, you should fork refl1d on github, and issue pull requests for each part. -Once you have create the fork, the clone line is slightly different:: - - git clone https://github.com/YourGithubAccount/refl1d - After you have tested your changes, you will need to push them to your github fork:: - git log git commit -a -m "short sentence describing what the change is for" git push @@ -97,12 +125,14 @@ track updates to the original refl1d package using:: git push When making changes, you need to take care that they work on different -versions of python. In particular, RHEL6, Centos6.5, Rocks and -ScientificLinux all run python 2.6, most linux/windows/mac users run -python 2.7, but some of the more bleeding edge distributions run 3.3/3.4. -The anaconda distribution makes it convenient to maintain multiple independent -environments -Even better is to test against all python versions 2.6, 2.7, 3.3, 3.4:: +versions of python. Using conda makes it convenient to maintain multiple independent +environments. You can create a new environment for testing with, for example:: + + conda create -n py312 python=3.12 + conda activate py312 + pip install -e .[dev] + +Even better is to test against all current python versions:: pythonX.Y tests.py pythonX.Y run.py @@ -113,7 +143,7 @@ Building Documentation ====================== Building the package documentation requires a working Sphinx installation, -and latex to build the pdf. As of this writing we are using sphinx 1.2. +and latex to build the pdf. As of this writing we are using sphinx 8.0.2. The command line to build the docs is as follows:: @@ -125,7 +155,7 @@ You can see the result by pointing your browser to:: doc/_build/latex/Refl1d.pdf Note that this only works with a unix-like environment for now since we are -using *make*. On windows, you can run sphinx directly from python:: +using *make*. On Windows, you can run sphinx directly from python:: cd doc python -m sphinx.__init__ -b html -d _build/doctrees . _build/html @@ -168,17 +198,17 @@ to a checkin on the master branch via GitHub Actions. OS/X Installer ============== -Note: OS/X installer is no longer maintained. +A Python script is available to build the OS/X installer:: -To build a Mac OS/X standalone executable you will need the py2app package. -This should already be available in your mac python environment. + extra/build_dmg.py -Build the executable using:: +This script builds a `.dmg` based on the contents of the `dist/.app` directory. +It can be called with the name and version of the product as arguments, e.g.:: - python setup_py2app + python extra/build_dmg.py Refl1D 0.8.17 -This creates a *.dmg* file in the *dist* directory with the Refl1D app -inside. +This script is also run automatically on github in response +to a checkin on the master branch via GitHub Actions. Creating a new release ---------------------- diff --git a/doc/getting_started/install.rst b/doc/getting_started/install.rst index 0d6031fd..7c7ee05e 100755 --- a/doc/getting_started/install.rst +++ b/doc/getting_started/install.rst @@ -1,16 +1,19 @@ .. _installing: -************************** -Installing the application -************************** +***************** +Installing Refl1D +***************** .. contents:: :local: | Windows installer: :slink:`%(winexe)s` | Source: :slink:`%(srczip)s` -Recent versions of the Refl1D application are available for windows -from `github `_. + +Installing Latest Release +========================= + +A Windows installer is available from `github `_. The file `Refl1D-VERSION-exe.zip` contains python, the application, the supporting libraries and everything else needed to run the application. @@ -25,91 +28,136 @@ The installed python is a full version of python. If your specialized reflectometry models need additional python packages, then you can use `python -m pip` in the extracted directory to install them. -Linux users will need to install from using pip:: +Refl1D is also available on all platforms from PyPI using pip:: - pip install refl1d wxpython + pip install refl1d + + # if you also want to run the webview, an optional extra is available + pip install refl1d[webview] -Note that the binary versions will lag the release version until the release -process is automated. Windows and Mac users may want to install using pip as -well to get the version with the latest -`changes `_. +.. note :: + The binary versions will lag the release version until the release + process is automated. Windows and Mac users may want to install using pip as + well to get the version with the latest `changes `_. Installing from source ====================== -Installing the application from source requires a working python environment. -See below for operating system specific instructions. +Requirements +------------ -Our base scientific python environment contains the following packages as -well as their dependencies: + - `Python `_ >= 3.10 + - git - - python 3 - - numpy - - scipy - - matplotlib - - wxpython +You can either use Python from the `python.org _` site, or if you prefer, +you can use miniforge. Miniforge is an open version of the `conda` program, and is pre-configured to use the `conda-forge` repository. +This repo is the most useful for open scientific application development. -Once your environment is in place, you can install directly from PyPI -using pip:: +You can get an installer for your system (MacOS, Windows or Linux) here: https://github.com/conda-forge/miniforge?tab=readme-ov-file#miniforge3 + +Run the installer, then from a terminal window (PowerShell or Terminal in Windows) run:: + + conda init + +then close and re-open your terminal. + + +Setup Environment +----------------- + +You can create a new virtual environment for Refl1D, or install it in your base environment. To create a new environment, run:: + + # if using miniforge + conda create -n refl1d + conda activate refl1d + + # if using regular python + python -m venv refl1d + source refl1d/bin/activate + + +Installation +------------ + +To install the application from source, clone the repository and install the +dependencies:: + + git clone https://github.com/reflectometry/refl1d.git + cd refl1d + pip install . + +If you want to run the webview, you can install the optional extra:: + + pip install .[webview] + + +Installing for Development +========================== + +Refl1d depends closely on the `bumps `_, +which also goes through frequent development. If you are also working with the +scattering length density calculator or the fitting engine, or if you need the +latest unreleased version of bumps, you may want to install bumps from source. +Clone the bumps repository and install from source in your refl1d virtual environment:: + + git clone https://github.com/bumps/bumps.git + pip install -e ./bumps - pip install refl1d -This will install refl1d, bumps and periodictable. +Python Environment +------------------ -You can run the program by typing: +If you are planning to contribute to the project, you will want to install +the package in development mode, including the dev dependencies:: - python -m refl1d.main + pip install -e .[dev] -If this fails, then follow the instructions in :ref:`contributing` to install -from the source archive directly. + # or if you plan to develop the webview + pip install -e .[dev,webview] -Windows -------- +This will install the package in development mode, so that changes you make +to the source code will be reflected in the installed package. It will also +install the development dependencies, which include the testing framework +and other tools used in the development process. -There are couple of options for setting up a python environment on windows: +Javascript Environment +---------------------- - - `python.org `_, and - - `Anaconda `_. +If you are planning to develop the webview (client), you will need to install +a Javascript environment. -With most pypi packages now bundled with wheels, it is now easy to set up a -development environment using the official python package. Similarly, -anaconda provides binaries for all the refl1d dependencies. +* `Node.js `_ can be installed from the website, or using conda:: -You will need a C/C++ compiler. If you already have Microsoft Visual C -installed you are done. If not, you can use the MinGW compiler that is supplied -with your python environment or download your own. You can set MinGW -as the default compiler by creating the file *Lib\distutils\\distutils.cfg* -in your python directory (e.g., *C:\\Python3.8*) with the following content:: + conda install -c conda-forge nodejs - [build] - compiler=mingw32 +* `Bun `_ is a fast-performing drop-in replacement for npm, and is available on all platforms. -Once the python is prepared, you can install the periodic table and bumps -package using the Windows console. +Similar to the Python environment, you may want to install and link the ``bumps-webview-client`` in your Refl1d Javascript environment:: -Linux ------ + cd /path/to/bumps/bumps/webview/client + npm install # or bun install + npm link # or bun link -Linux distributions will provide the base required packages. You -will need to refer to your distribution documentation for details. + cd /path/to/refl1d/refl1d/webview/client + npm install # or bun install + npm link bumps-webview-client # or bun link bumps-webview-client -On debian/ubuntu, the command will be something like:: +To build the client, run:: - sudo apt-get install python3-{matplotlib,numpy,scipy,wxgtk4.0,pyparsing,setuptools} + cd /path/to/refl1d/refl1d/webview/client + npm run build # or bun build -For development you also want nose and sphinx:: +If you are developing the client, you can run the client in development mode. +In this mode, any changes to client code are immediately reflected in a connected running client:: - sudo apt-get install python-{nose,sphinx} + npm run dev # or bun run dev -Latex is needed to build the pdf documentation. +This starts the client and shows the URL to connect to in the terminal (typically http://localhost:5173). -OS/X ----- +Now, you can start the Python webview server with:: -Similar to windows, you can install the official python distribution or -use Anaconda. You will need to install the Xcode command line utilities -to get the compiler. + refl1d-webview --headless --port 8080 -To run the interactive interface on OS/X you may need to use:: +and point the client to the server with the `?server=localhost:8080` query string, e.g. - pythonw -m refl1d.main --edit + http://localhost:5173/?server=localhost:8080 diff --git a/doc/getting_started/server.rst b/doc/getting_started/server.rst index ef30e553..a0326de6 100644 --- a/doc/getting_started/server.rst +++ b/doc/getting_started/server.rst @@ -11,5 +11,3 @@ You just need to install the refl1d plugin in the bumps server. TODO: show details. - - diff --git a/doc/pylit.py b/doc/pylit.py index eb259bba..5f5a0b86 100755 --- a/doc/pylit.py +++ b/doc/pylit.py @@ -113,8 +113,6 @@ # # :: -from __future__ import print_function - _version = "0.7.9" __docformat__ = "restructuredtext" @@ -137,8 +135,10 @@ # # :: -import os, sys -import re, optparse +import os # noqa: E402 +import sys # noqa: E402 +import re # noqa: E402 +import optparse # noqa: E402 # DefaultDict @@ -367,7 +367,7 @@ def __getitem__(self, key): # Try to import optional extensions:: try: - import pylit_elisp + import pylit_elisp # type: ignore # noqa: F401 except ImportError: pass diff --git a/doc/requirements.txt b/doc/requirements.txt index ab6dd2a2..1b3a6e86 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,7 +1 @@ -setuptools -numpy>=1.0 -scipy>=0.7.0 -matplotlib>=1.0 -numba -bumps -periodictable +sphinx \ No newline at end of file diff --git a/refl1d/corrtest.py b/explore/corrtest.py similarity index 98% rename from refl1d/corrtest.py rename to explore/corrtest.py index c179fbc3..27380abc 100644 --- a/refl1d/corrtest.py +++ b/explore/corrtest.py @@ -14,11 +14,10 @@ which is anyway already covered by the chisq cost function. """ -from __future__ import print_function - from scipy import stats import numpy as np +from refl1d.material import Vacuum def residual_nllf(v): # Normalize the scores so that we are assuming diff --git a/explore/interface.py b/explore/interface.py index ab95e048..14d663fd 100644 --- a/explore/interface.py +++ b/explore/interface.py @@ -93,8 +93,12 @@ def default(cls, value, **kw): return cls(value) -sech = lambda x: 1 / cosh(x) -asech = lambda x: acosh(1 / x) +def sech(x): + return 1 / cosh(x) + + +def asech(x): + return acosh(1 / x) class Interface(object): diff --git a/refl1d/refl_tr.py b/explore/refl_tr.py similarity index 98% rename from refl1d/refl_tr.py rename to explore/refl_tr.py index 93dc3aa8..d73a0c55 100644 --- a/refl1d/refl_tr.py +++ b/explore/refl_tr.py @@ -1,4 +1,3 @@ -# This program is public domain. r""" Optical matrix form of the reflectivity calculation. @@ -35,10 +34,8 @@ [4] https://en.wikipedia.org/wiki/Prefix_sum """ -from __future__ import print_function, division - import numpy as np -from numpy import asarray, isscalar, empty, ones, ones_like +from numpy import asarray, isscalar, ones, ones_like from numpy import sqrt, exp, pi @@ -226,7 +223,7 @@ def check(): rho *= 100 # show point below critical edge print("q", q) try: - from .abeles import refl + from ..refl1d.abeles import refl r_old = refl(q / 2, depth, rho, irho=irho, sigma=sigma) print("rold", r_old) diff --git a/extra/gen_schema.py b/extra/gen_schema.py index d55819a0..0b426b9b 100644 --- a/extra/gen_schema.py +++ b/extra/gen_schema.py @@ -1,3 +1,10 @@ +""" +Generate a JSON schema for the FitProblem model. +Note: +This script is likely deprecated, and relies on pydantic v1, +as it makes use of functionality that have been removed in v2. +""" + import os import math import json diff --git a/extra/gen_schema_v2.py b/extra/gen_schema_v2.py index 130c8e66..7e4aab84 100644 --- a/extra/gen_schema_v2.py +++ b/extra/gen_schema_v2.py @@ -1,16 +1,17 @@ -from pydantic_core import core_schema -from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue -from pydantic import TypeAdapter, ConfigDict +import json from bumps.parameter import * from bumps.util import NumpyArray - -NDArray = NumpyArray +from pydantic_core import core_schema +from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue +from pydantic import TypeAdapter from refl1d.names import * from refl1d.model import * from refl1d.fitproblem import FitProblem +NDArray = NumpyArray + class BumpsGenerateJsonSchema(GenerateJsonSchema): def dataclass_schema(self, schema: core_schema.DataclassSchema) -> JsonSchemaValue: @@ -30,6 +31,4 @@ def dataclass_schema(self, schema: core_schema.DataclassSchema) -> JsonSchemaVal TA = TypeAdapter(FitProblem) schema = TA.json_schema(schema_generator=BumpsGenerateJsonSchema) -import json - print(json.dumps(schema, indent=2)) diff --git a/pyproject.toml b/pyproject.toml index 92294b9a..a187181f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,15 @@ dependencies = [ ] [project.optional-dependencies] -dev = ["pre-commit", "ruff", "pytest", "pytest-cov", "versioningit"] +dev = [ + "pre-commit", + "pydantic", + "pytest", + "pytest-cov", + "ruff", + "sphinx", + "versioningit", +] full = ["wxpython", "ipython"] webview = ["bumps[webview]"] @@ -46,9 +54,7 @@ build-backend = "setuptools.build_meta" [tool.ruff] line-length = 120 -exclude = [ - "*.txt", -] +exclude = ["*.txt"] # [tool.ruff.lint] # select = [ diff --git a/refl1d/abeles.py b/refl1d/abeles.py index 13beb112..a664fd91 100644 --- a/refl1d/abeles.py +++ b/refl1d/abeles.py @@ -9,9 +9,7 @@ application uses reflmodule to compute reflectivity. """ -from __future__ import print_function, division - -from numpy import asarray, isscalar, empty, ones, ones_like +from numpy import asarray, isscalar, empty, ndarray, ones, ones_like from numpy import sqrt, exp, pi @@ -74,8 +72,8 @@ def refl(kz, depth, rho, irho=0, sigma=0, rho_index=None): return r -def _calc(kz, depth, rho, irho, sigma): - # type: (np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray) -> np.ndarray +def _calc(kz: ndarray, depth: ndarray, rho: ndarray, irho: ndarray, sigma: ndarray) -> ndarray: + """Reflectivity as a function of kz for a set of slabs.""" if len(kz) == 0: return kz diff --git a/refl1d/anstodata.py b/refl1d/anstodata.py index bb79b06c..8ca8fbd0 100644 --- a/refl1d/anstodata.py +++ b/refl1d/anstodata.py @@ -10,11 +10,12 @@ All the ANSTO instruments emit Q/R/dR/dQ in their output files. """ -import re import os.path -import numpy as np +import re +import numpy as np from bumps.data import maybe_open + from .probe import QProbe from .resolution import FWHM2sigma diff --git a/refl1d/cheby.py b/refl1d/cheby.py index 7143a642..c3f0c7a0 100644 --- a/refl1d/cheby.py +++ b/refl1d/cheby.py @@ -77,14 +77,15 @@ # - Newton methods: Hessian should point back to domain # - Direct methods: random walk should be biased toward the domain # - moderately complicated + import numpy as np -from numpy import inf, real, imag -from bumps.parameter import Parameter as Par, to_dict -from bumps.cheby import cheby_val, cheby_coeff -from bumps.cheby import cheby_approx, cheby_points # pylint: disable=unused-import +from bumps.cheby import cheby_approx, cheby_coeff, cheby_points, cheby_val # pylint: disable=unused-import +from bumps.parameter import Parameter as Par +from bumps.parameter import to_dict +from numpy import imag, inf, real -from .model import Layer from . import util +from .model import Layer # TODO: add left_sld, right_sld to all layers so that fresnel works diff --git a/refl1d/errors.py b/refl1d/errors.py index 9a5019b2..3e01e7ac 100644 --- a/refl1d/errors.py +++ b/refl1d/errors.py @@ -9,27 +9,25 @@ Use *run_errors* in a model file to reload the results of a batch DREAM fit. """ -from __future__ import print_function - __all__ = [ + "align_profiles", + "calc_errors", "reload_errors", "run_errors", - "calc_errors", - "align_profiles", "show_errors", "show_profiles", "show_residuals", ] -import sys import os +import sys import numpy as np -from bumps.plotutil import next_color, dhsv, plot_quantiles, form_quantiles from bumps.errplot import reload_errors +from bumps.plotutil import dhsv, form_quantiles, next_color, plot_quantiles -from .util import asbytes from .reflectivity import BASE_GUIDE_ANGLE +from .util import asbytes # CONTOURS = (68, 95, 100) # CONTOURS = (57, 68, 84, 95, 100) diff --git a/refl1d/fasta.py b/refl1d/fasta.py index c9b59dce..474fa47e 100644 --- a/refl1d/fasta.py +++ b/refl1d/fasta.py @@ -36,8 +36,6 @@ """ -from __future__ import division, print_function - import periodictable as pt diff --git a/refl1d/flayer.py b/refl1d/flayer.py index a3b7f46a..608c65ad 100644 --- a/refl1d/flayer.py +++ b/refl1d/flayer.py @@ -1,12 +1,12 @@ import inspect -from numpy import real, imag, asarray, broadcast_to +from bumps.parameter import Calculation, Parameter, to_dict +from numpy import asarray, broadcast_to, imag, real -from bumps.parameter import Parameter, to_dict, Calculation +from refl1d import util +from refl1d.magnetism import DEFAULT_THETA_M, BaseMagnetism, Magnetism from refl1d.material import SLD from refl1d.model import Layer -from refl1d.magnetism import BaseMagnetism, Magnetism, DEFAULT_THETA_M -from refl1d import util class FunctionalProfile(Layer): diff --git a/refl1d/fresnel.py b/refl1d/fresnel.py index 80ac2adb..eec79811 100644 --- a/refl1d/fresnel.py +++ b/refl1d/fresnel.py @@ -1,10 +1,8 @@ -# This code is public domain - """ Pure python Fresnel reflectivity calculator. """ -from numpy import sqrt, exp, real, conj, pi, abs, choose +from numpy import sqrt, exp, conj, pi, abs, choose class Fresnel(object): diff --git a/refl1d/instrument.py b/refl1d/instrument.py index 7347dd73..7b094c27 100644 --- a/refl1d/instrument.py +++ b/refl1d/instrument.py @@ -122,13 +122,10 @@ calculation can be performed. """ -from __future__ import division, print_function - # TODO: the resolution calculator should not be responsible for loading # the data; maybe do it as a mixin? import numpy as np -# from numpy import pi, inf, sqrt, log, degrees, radians, cos, sin, tan from .resolution import QL2T from .resolution import bins, binwidths, binedges diff --git a/refl1d/lib/numba/clone_module.py b/refl1d/lib/numba/clone_module.py index 125b44ad..aa192c80 100644 --- a/refl1d/lib/numba/clone_module.py +++ b/refl1d/lib/numba/clone_module.py @@ -1,4 +1,3 @@ -import numba import importlib.util diff --git a/refl1d/lib/python/magnetic.py b/refl1d/lib/python/magnetic.py index 1e2eb257..afb1db69 100644 --- a/refl1d/lib/python/magnetic.py +++ b/refl1d/lib/python/magnetic.py @@ -1,5 +1,5 @@ import sys -from numpy import pi, sin, cos, conj, radians, sqrt, exp, fabs +from numpy import pi, sin, cos, radians, sqrt, exp, fabs EPS = sys.float_info.epsilon M_PI = pi diff --git a/refl1d/magnetism.py b/refl1d/magnetism.py index 0cb5a8b3..56a15db1 100644 --- a/refl1d/magnetism.py +++ b/refl1d/magnetism.py @@ -1,5 +1,3 @@ -# This program is public domain -# Author: Paul Kienzle r""" Magnetic modeling for 1-D reflectometry. @@ -38,15 +36,12 @@ and anchoring them to the structure. """ -from __future__ import print_function - -from dataclasses import dataclass, field -from typing import Optional, Any, Union, Dict, Callable, Literal, Tuple, List, Literal +from dataclasses import dataclass +from typing import Dict, List, Literal, Union import numpy as np -from numpy import inf -from bumps.parameter import Parameter, flatten, to_dict from bumps.mono import monospline +from bumps.parameter import Parameter, flatten, to_dict from .reflectivity import BASE_GUIDE_ANGLE as DEFAULT_THETA_M diff --git a/refl1d/material.py b/refl1d/material.py index 5cd7ec3d..b8601acf 100644 --- a/refl1d/material.py +++ b/refl1d/material.py @@ -1,5 +1,3 @@ -# This program is in the public domain -# Author Paul Kienzle """ Reflectometry materials. @@ -55,14 +53,14 @@ "CellVolumeMaterial", ] -from dataclasses import dataclass, field -from typing import Optional, Any, Union, Dict, Callable, Literal, Tuple, List, Literal +from dataclasses import dataclass +from typing import Literal, Optional, Tuple, Union import numpy as np -from numpy import inf, nan import periodictable +from bumps.parameter import Expression, Parameter +from numpy import inf, nan from periodictable.constants import avogadro_number -from bumps.parameter import Expression, Parameter, to_dict # , PARAMETER_TYPES, UnaryExpression from periodictable.formulas import Formula as BaseFormula @@ -163,16 +161,6 @@ def __init__(self, name="SLD", rho: Union[float, Parameter] = 0, irho=0): def parameters(self): return {"rho": self.rho, "irho": self.irho} - def to_dict(self): - return to_dict( - { - "type": type(self).__name__, - "name": self.name, - "rho": self.rho, - "irho": self.irho, - } - ) - def sld(self, probe): return self.rho.value, self.irho.value @@ -531,13 +519,6 @@ def parameters(self): """ return {"count": self.count} - def to_dict(self): - return { - "type": type(self).__name__, - "parts": to_dict(self.parts), - "count": to_dict(self.count), - } - def formula(self): return tuple((c.value, f) for c, f in zip(self.count, self.parts)) @@ -661,14 +642,6 @@ def parameters(self): "fraction": self.fraction, } - def to_dict(self): - return { - "type": type(self).__name__, - "base": to_dict(self.base), - "material": to_dict(self.material), - "fraction": to_dict(self.fraction), - } - def _density(self): """ Compute the density of the mixture from the density and proportion diff --git a/refl1d/model.py b/refl1d/model.py index 2c242057..e321b4ea 100644 --- a/refl1d/model.py +++ b/refl1d/model.py @@ -1,5 +1,3 @@ -# This program is in the public domain -# Author: Paul Kienzle """ Reflectometry models @@ -24,17 +22,11 @@ __all__ = ["Repeat", "Slab", "Stack", "Layer"] -from copy import copy, deepcopy +from copy import copy from dataclasses import dataclass, field -import json -from typing import Optional, Any, Union, Dict, Callable, Literal, Tuple, List, Literal +from typing import List, Literal, Optional, Union import numpy as np -from numpy import inf, nan, pi, sin, cos, tan, sqrt, exp, log, log10, degrees, radians, floor, ceil -import periodictable -import periodictable.xsf as xsf -import periodictable.nsf as nsf - from bumps.parameter import ( # BaseParameter as BasePar, Calculation, @@ -131,19 +123,6 @@ def __str__(self): """ return getattr(self, "name", repr(self)) - def to_dict(self): - """ - Return a dictionary representation of the Slab object - """ - raise NotImplementedError("to_dict not defined for " + str(self)) - # return to_dict({ - # 'type': type(self).__name__, - # 'name': self.name, - # 'thickness': self.thickness, - # 'interface': self.interface, - # 'magnetism': self.magnetism, - # }) - # Define a little algebra for composing samples # Layers can be stacked, repeated, or have length/roughness/magnetism set def __or__(self, other): @@ -244,21 +223,6 @@ def __str__(self): def __repr__(self): return "Slab(" + repr(self.material) + ")" - def to_dict(self): - """ - Return a dictionary representation of the Slab object - """ - return to_dict( - { - "type": type(self).__name__, - "name": self.name, - "thickness": self.thickness, - "interface": self.interface, - "material": self.material, - "magnetism": self.magnetism, - } - ) - @dataclass(init=False) class Stack(Layer): @@ -356,19 +320,6 @@ def __str__(self): def __repr__(self): return "Stack(" + ", ".join(repr(L) for L in self._layers) + ")" - def to_dict(self): - """ - Return a dictionary representation of the Stack object - """ - return to_dict( - { - "type": type(self).__name__, - "name": self.name, - "interface": self.interface, - "layers": self._layers, - } - ) - def parameters(self): layers = [L.layer_parameters() for L in self._layers] return {"thickness": self.thickness, "layers": layers} @@ -419,7 +370,7 @@ def _render_magnetic(self, probe, slabs): # import sys; print >>sys.stderr, "magnetism", magnetism anchor = slabs.thickness() + magnetism.dead_below.value s_below = ( - nan + np.nan if i == 0 else magnetism.interface_below.value if magnetism.interface_below is not None @@ -445,7 +396,8 @@ def _render_magnetic(self, probe, slabs): def _plot(self, dz=1, roughness_limit=0): # TODO: unused? import matplotlib.pyplot as plt - from . import profile, material, probe + + from . import material, probe, profile neutron_probe = probe.NeutronProbe(T=np.arange(0, 5, 100), L=5.0) xray_probe = probe.XrayProbe(T=np.arange(0, 5, 100), L=1.54) @@ -643,10 +595,8 @@ class Repeat(Layer): def __init__( self, stack, repeat=1, interface=None, name=None, magnetism=None, thickness: Optional[Parameter] = None ): - if name is None: - name = "multilayer" - if interface is None: - interface = stack[-1].interface.value + name = "multilayer" if name is None else name + interface = stack[-1].interface.value if interface is None else interface self.magnetism = magnetism self.name = name self.repeat = Parameter(repeat, limits=(0, None), discrete=True, name=name + " repeats") @@ -657,21 +607,6 @@ def __init__( self.thickness.equals(self.stack.thickness * self.repeat) self.interface = Parameter.default(interface, limits=(0, None), name=name + " top interface") - def to_dict(self): - """ - Return a dictionary representation of the Repeat object - """ - return to_dict( - { - "type": type(self).__name__, - "name": self.name, - "interface": self.interface, - "magnetism": self.magnetism, - "repeat": self.repeat, - "stack": self.stack, - } - ) - def __setstate__(self, state): # this is a temporary shim (2024-10-29), to accomodate objects that were pickled # before the custom __getstate__ was removed. diff --git a/refl1d/mono.py b/refl1d/mono.py index e5dc94f4..12d6f7d6 100644 --- a/refl1d/mono.py +++ b/refl1d/mono.py @@ -2,14 +2,15 @@ Monotonic spline modeling for free interfaces """ -from __future__ import division, with_statement +from dataclasses import dataclass +from typing import Any, List, Optional, Union -from dataclasses import dataclass, field -from typing import Optional, Any, Union, Dict, Callable, Literal, Tuple, List, Literal import numpy as np -from numpy import diff, hstack, sqrt, searchsorted, asarray, cumsum, inf, nonzero, linspace, sort, isnan, clip -from bumps.parameter import Parameter as Par, Function as ParFunction, to_dict, Constant -from bumps.mono import monospline, count_inflections +from bumps.mono import count_inflections, monospline +from bumps.parameter import Constant, to_dict +from bumps.parameter import Function as ParFunction +from bumps.parameter import Parameter as Par +from numpy import asarray, clip, cumsum, diff, hstack, inf, sort from . import util from .model import Layer diff --git a/refl1d/names.py b/refl1d/names.py index 84f19437..fb4b7a24 100644 --- a/refl1d/names.py +++ b/refl1d/names.py @@ -11,36 +11,35 @@ """ import sys -import numpy as np -from periodictable import elements, formula -from bumps.parameter import Parameter, FreeVariables +import numpy as np from bumps import pmath -from bumps.pdfwrapper import PDF from bumps.fitproblem import FitProblem, MultiFitProblem -# from bumps.fitproblem import FitProblem # deprecated +from bumps.parameter import FreeVariables, Parameter +from bumps.pdfwrapper import PDF +from periodictable import elements, formula -from .experiment import Experiment, plot_sample, MixedExperiment -from .flayer import FunctionalProfile, FunctionalMagnetism -from .material import SLD, Material, Compound, Mixture -from .model import Slab, Stack -from .polymer import PolymerBrush, PolymerMushroom, EndTetheredPolymer, VolumeProfile, layer_thickness -from .mono import FreeLayer, FreeInterface -from .cheby import FreeformCheby, ChebyVF, cheby_approx, cheby_points -from .probe import Probe, ProbeSet, XrayProbe, NeutronProbe, QProbe, PolarizedNeutronProbe, PolarizedQProbe, load4 -from .stajconvert import load_mlayer, save_mlayer -from . import ncnrdata as NCNR, snsdata as SNS +from . import ncnrdata as NCNR +from . import snsdata as SNS +from .cheby import ChebyVF, FreeformCheby, cheby_approx, cheby_points +from .experiment import Experiment, MixedExperiment, plot_sample +from .flayer import FunctionalMagnetism, FunctionalProfile from .instrument import Monochromatic, Pulsed -from .magnetic import MagneticSlab, MagneticTwist, FreeMagnetic, MagneticStack -from .magnetism import Magnetism, MagnetismTwist, FreeMagnetism, MagnetismStack -from .support import sample_data +from .magnetic import FreeMagnetic, MagneticSlab, MagneticStack, MagneticTwist +from .magnetism import FreeMagnetism, Magnetism, MagnetismStack, MagnetismTwist +from .material import SLD, Compound, Material, Mixture # Pull in common materials for reflectometry experiments. # This could lead to a lot of namespace pollution, and particularly to # confusion if the user also does "from periodictable import *" since # both of them create elements. -# Python doesn't allow "from .module import *" from .materialdb import * +from .model import Slab, Stack +from .mono import FreeInterface, FreeLayer +from .polymer import EndTetheredPolymer, PolymerBrush, PolymerMushroom, VolumeProfile, layer_thickness +from .probe import NeutronProbe, PolarizedNeutronProbe, PolarizedQProbe, Probe, ProbeSet, QProbe, XrayProbe, load4 +from .stajconvert import load_mlayer, save_mlayer +from .support import sample_data # Deprecated names diff --git a/refl1d/oversampling.py b/refl1d/oversampling.py index 4973bf11..9739353a 100644 --- a/refl1d/oversampling.py +++ b/refl1d/oversampling.py @@ -1,6 +1,7 @@ -import numpy as np import argparse +import numpy as np + def get_optimal_single_oversampling(model, tolerance=0.05, max_oversampling=201, seed=1, verbose=False): """ diff --git a/refl1d/polymer.py b/refl1d/polymer.py index b6e0fb3a..5e589908 100644 --- a/refl1d/polymer.py +++ b/refl1d/polymer.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -# This program is public domain -# Authors Paul Kienzle, Richard Sheridan r""" Layer models for polymer systems. @@ -45,17 +43,14 @@ """ -from __future__ import division, print_function, unicode_literals - - __all__ = ["PolymerBrush", "PolymerMushroom", "EndTetheredPolymer", "VolumeProfile", "layer_thickness"] import inspect -from time import time from collections import OrderedDict +from time import time import numpy as np -from numpy import real, imag, exp, log, sqrt, pi, hstack, ones_like +from numpy import exp, hstack, imag, log, ones_like, pi, real, sqrt try: from numpy._core.multiarray import correlate as old_correlate @@ -463,6 +458,7 @@ def render(self, probe, slabs): M = Mr + 1j * Mi S = Sr + 1j * Si try: + # TODO: Fix this hack M, S = M[0], S[0] # Temporary hack except: pass diff --git a/refl1d/probe.py b/refl1d/probe.py index 500bc34f..e8d80030 100644 --- a/refl1d/probe.py +++ b/refl1d/probe.py @@ -1,7 +1,4 @@ -# coding=utf-8 -# This program is in the public domain -# Author: Paul Kienzle -r""" +""" Experimental probe. The experimental probe describes the incoming beam for the experiment. @@ -12,7 +9,9 @@ """ -from __future__ import with_statement, division, print_function +from __future__ import division, print_function, with_statement + +import json # TOF stitching introduces a lot of complexity. # Theta offset: @@ -38,36 +37,29 @@ # profiles are recalculated # # Unstitched seems like the better bet. - import os -import json import warnings from dataclasses import dataclass -from enum import Enum +from typing import TYPE_CHECKING, Any, List, Literal, Optional, Sequence, Tuple, Union import numpy as np -from numpy import sqrt, pi, inf, sign, log -import numpy.random import numpy.fft -from typing import NamedTuple, Optional, Any, Sequence, Union, TYPE_CHECKING - -from bumps.util import field, field_desc, Optional, Any, Union, Dict, Callable, Literal, Tuple, List, Literal - -from periodictable import nsf, xsf -from bumps.parameter import Parameter, to_dict, Constant -from bumps.plotutil import coordinated_colors, auto_shift +import numpy.random from bumps.data import parse_multi, strip_quotes -from bumps.util import USE_PYDANTIC +from bumps.parameter import Parameter, to_dict +from bumps.plotutil import auto_shift, coordinated_colors +from bumps.util import USE_PYDANTIC, field, field_desc +from numpy import log, pi, sign, sqrt +from periodictable import nsf, xsf if USE_PYDANTIC or TYPE_CHECKING: from bumps.util import NDArray from . import fresnel from .material import Vacuum -from .resolution import QL2T, QT2L, TL2Q, dQdL2dT, dQdT2dLoL, dTdL2dQ -from .resolution import sigma2FWHM, FWHM2sigma, dQ_broadening +from .reflectivity import BASE_GUIDE_ANGLE, convolve +from .resolution import QL2T, QT2L, TL2Q, FWHM2sigma, dQ_broadening, dQdL2dT, dQdT2dLoL, dTdL2dQ, sigma2FWHM from .stitch import stitch -from .reflectivity import convolve, BASE_GUIDE_ANGLE from .util import asbytes PROBE_KW = ( @@ -121,8 +113,8 @@ class BaseProbe: @property def calc_Q(self): - """define in derived classes""" - raise NotImplemented + """Define in derived classes""" + raise NotImplementedError def log10_to_linear(self): """ diff --git a/refl1d/profile.py b/refl1d/profile.py index 22b7f95c..854a2416 100644 --- a/refl1d/profile.py +++ b/refl1d/profile.py @@ -1,6 +1,4 @@ -# This program is public domain -# Author: Paul Kienzle -r""" +""" Scattering length density profile. In order to render a reflectometry model, the theory function calculator @@ -58,10 +56,8 @@ irho = 0 0 0 0 0 ... """ -from __future__ import division, print_function - import numpy as np -from numpy import inf, nan, isnan +from numpy import isnan, nan from scipy.special import erf from .reflectivity import BASE_GUIDE_ANGLE as DEFAULT_THETA_M diff --git a/refl1d/reflectivity.py b/refl1d/reflectivity.py index c977f134..cedb3be7 100644 --- a/refl1d/reflectivity.py +++ b/refl1d/reflectivity.py @@ -10,8 +10,6 @@ of the magnetic scattering using an unpolarized beam. """ -from __future__ import print_function - from functools import reduce # __doc__ = "Fundamental reflectivity calculations" @@ -26,7 +24,7 @@ ] import numpy as np -from numpy import pi, sin, cos, conj, radians, sqrt, exp, fabs +from numpy import sin, cos, conj, radians BASE_GUIDE_ANGLE = 270.0 diff --git a/refl1d/refllib.py b/refl1d/refllib.py index bb26a92e..c9ca55b9 100644 --- a/refl1d/refllib.py +++ b/refl1d/refllib.py @@ -1,6 +1,4 @@ -# This program is public domain -# Authors: Paul Kienzle and Brian Maranville -r""" +""" Reflectometry backend loader """ @@ -8,8 +6,8 @@ from . import BACKEND_NAME, BACKEND_NAMES BACKEND_MODULE_NAMES = { - "numba": "refl1d.lib.numba", "c_ext": "refl1d.reflmodule", + "numba": "refl1d.lib.numba", "python": "refl1d.lib.python", } @@ -20,7 +18,7 @@ def set_backend(backend_name: BACKEND_NAMES): global backend backend_module_name = BACKEND_MODULE_NAMES.get(backend_name, None) if backend_module_name is None: - raise ValueError(f"unknown backend: {backend_name}") + raise ValueError(f"Unknown backend: {backend_name}") backend = importlib.import_module(backend_module_name) diff --git a/refl1d/resolution.py b/refl1d/resolution.py index a83a5116..9967d871 100644 --- a/refl1d/resolution.py +++ b/refl1d/resolution.py @@ -1,14 +1,28 @@ -r""" +""" Resolution calculations - """ -from numpy import pi, sqrt, log, degrees, radians, cos, sin, tan -from numpy import arcsin as asin, ceil -from numpy import ones_like, arange, isscalar, asarray, hstack -from numpy import float64 from typing import TYPE_CHECKING +from numpy import ( + arange, + asarray, + ceil, + cos, + degrees, + float64, + hstack, + isscalar, + log, + ones_like, + pi, + radians, + sin, + sqrt, + tan, +) +from numpy import arcsin as asin + if TYPE_CHECKING: from numpy.typing import ArrayLike diff --git a/refl1d/staj.py b/refl1d/staj.py index 9b597e57..7a0884c9 100644 --- a/refl1d/staj.py +++ b/refl1d/staj.py @@ -1,6 +1,4 @@ -# This program is in the public domain -# Author: Paul Kienzle -r""" +""" Read and write staj files Staj files are the model files for the mlayer and gj2 programs, which are diff --git a/refl1d/stajconvert.py b/refl1d/stajconvert.py index b3414d96..d6c0ae65 100644 --- a/refl1d/stajconvert.py +++ b/refl1d/stajconvert.py @@ -1,5 +1,3 @@ -# This program is in the public domain -# Author: Paul Kienzle """ Convert staj files to Refl1D models """ diff --git a/refl1d/support.py b/refl1d/support.py index e79e59df..94bac905 100644 --- a/refl1d/support.py +++ b/refl1d/support.py @@ -7,6 +7,7 @@ """ import os +from pathlib import Path def get_data_path(): @@ -23,8 +24,8 @@ def get_data_path(): # Check for data next to the package. try: - root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - return os.path.join(root, "doc/examples") + root = Path(__file__).resolve().parent.parent.parent + return root / "doc" / "examples" except Exception: raise RuntimeError("Could not find sample data") diff --git a/refl1d/util.py b/refl1d/util.py index a7641d6f..9fab785b 100644 --- a/refl1d/util.py +++ b/refl1d/util.py @@ -1,18 +1,10 @@ __all__ = ["merge_ends"] -import sys - import numpy as np -# CRUFT: 2.7 support -if sys.version_info[0] > 2: - - def asbytes(s): - return s.encode("utf-8") -else: - def asbytes(s): - return s +def asbytes(s): + return s.encode("utf-8") def merge_ends(w, p, tol=1e-3): diff --git a/refl1d/view/data_view.py b/refl1d/view/data_view.py index 55ebeaab..6b4f1399 100755 --- a/refl1d/view/data_view.py +++ b/refl1d/view/data_view.py @@ -1,25 +1,19 @@ -from __future__ import with_statement - +import matplotlib.pyplot as plt import wx - -# Can't seem to detect when notebook should be drawn on Mac -IS_MAC = wx.Platform == "__WXMAC__" - -from numpy import inf - +from bumps.fitproblem import FitProblem +from bumps.gui.util import EmbeddedPylab from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as Toolbar # The Figure object is used to create backend-independent plot representations. from matplotlib.figure import Figure - -import matplotlib.pyplot as plt - -from bumps.gui.util import EmbeddedPylab -from bumps.fitproblem import FitProblem +from numpy import inf from refl1d.probe import Probe +# Can't seem to detect when notebook should be drawn on Mac +IS_MAC = wx.Platform == "__WXMAC__" + # ------------------------------------------------------------------------ class DataView(wx.Panel): diff --git a/refl1d/view/interactor.py b/refl1d/view/interactor.py index e3ebb4a6..446b97c2 100644 --- a/refl1d/view/interactor.py +++ b/refl1d/view/interactor.py @@ -10,9 +10,10 @@ def safecall(fn): def wrapped(self, *args, **kw): try: fn(self, *args, **kw) - except: + except Exception: if self._debug: - import sys, traceback + import sys + import traceback traceback.print_exc() sys.exit(1) diff --git a/refl1d/view/layer.py b/refl1d/view/layer.py index 7dbd0453..609d21d7 100644 --- a/refl1d/view/layer.py +++ b/refl1d/view/layer.py @@ -2,12 +2,12 @@ Layer interactor. """ -from __future__ import division from bumps.parameter import Parameter + from refl1d.material import SLD + +from .config import disabled_color, pick_radius, rho_color, rhoI_color from .interactor import BaseInteractor -from .config import pick_radius -from .config import rho_color, rhoI_color, disabled_color from .util import setpar diff --git a/refl1d/view/layer_dialog.py b/refl1d/view/layer_dialog.py index 51f400f5..f8af4e7c 100644 --- a/refl1d/view/layer_dialog.py +++ b/refl1d/view/layer_dialog.py @@ -1,11 +1,9 @@ import wx - -from numpy import inf - from bumps.gui.input_list import InputListPanel +from numpy import inf -from refl1d.names import Slab, FreeLayer, FreeInterface, PolymerBrush from refl1d.material import SLD +from refl1d.names import FreeInterface, FreeLayer, PolymerBrush, Slab # name, type, description, *args # type: 'string' @@ -269,7 +267,7 @@ def OnInit(self): if __name__ == "__main__": - from refl1d.names import silicon, air + from refl1d.names import air, silicon stack = silicon | air main(stack) diff --git a/refl1d/view/model_view.py b/refl1d/view/model_view.py index 1cde1df8..e12a3ea9 100644 --- a/refl1d/view/model_view.py +++ b/refl1d/view/model_view.py @@ -3,26 +3,26 @@ """ import os -import wx - -IS_MAC = wx.Platform == "__WXMAC__" import numpy as np -from matplotlib.figure import Figure +import wx +from bumps.fitproblem import FitProblem +from bumps.gui import signal from matplotlib.axes import Subplot -from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas from matplotlib.backend_bases import FigureManagerBase +from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg - -from bumps.fitproblem import FitProblem -from bumps.gui import signal +from matplotlib.figure import Figure from refl1d.experiment import MixedExperiment +from .interactor import BaseInteractor +from .profilei import ProfileInteractor + # from .binder import pixel_to_data from .util import CopyImage -from .profilei import ProfileInteractor -from .interactor import BaseInteractor + +IS_MAC = wx.Platform == "__WXMAC__" # ------------------------------------------------------------------------ @@ -82,7 +82,9 @@ def __init__(self, *args, **kw): self.statusbar = frame.GetStatusBar() if self.statusbar is None: self.statusbar = frame.CreateStatusBar() - status_update = lambda msg: self.statusbar.SetStatusText(msg) + + def status_update(msg): + return self.statusbar.SetStatusText(msg) # Set the profile interactor self.profile = ProfileInteractor(self.axes, self.theta_axes, status_update=status_update) diff --git a/refl1d/view/polymeri.py b/refl1d/view/polymeri.py index b4fc33dc..9bcb44ca 100644 --- a/refl1d/view/polymeri.py +++ b/refl1d/view/polymeri.py @@ -1,11 +1,10 @@ -from __future__ import division from math import log + import numpy + +from .config import pick_radius, profile_color, vf_scale from .interactor import BaseInteractor from .layer import MaterialInteractor -from .config import pick_radius -from .config import profile_color -from .config import vf_scale from .util import setpar diff --git a/refl1d/view/profilei.py b/refl1d/view/profilei.py index daff73ce..12916944 100644 --- a/refl1d/view/profilei.py +++ b/refl1d/view/profilei.py @@ -2,22 +2,18 @@ Reflectometry profile interactor. """ -from __future__ import division, print_function - import math import numpy as np +from matplotlib import transforms from numpy import inf -from .binder import BindArtist, pixel_to_data -from .config import rho_color, rhoI_color, rhoM_color, thetaM_color -from .config import layer_hysteresis from . import registry +from .binder import BindArtist, pixel_to_data +from .config import layer_hysteresis, rho_color, rhoI_color, rhoM_color, thetaM_color from .interactor import BaseInteractor, safecall -from .thickness import ThicknessInteractor from .interface import InterfaceInteractor - -from matplotlib import transforms +from .thickness import ThicknessInteractor blend_xy = transforms.blended_transform_factory diff --git a/refl1d/view/registry.py b/refl1d/view/registry.py index 7da25a5c..2f79ede8 100644 --- a/refl1d/view/registry.py +++ b/refl1d/view/registry.py @@ -1,9 +1,9 @@ from ..model import Slab -from ..polymer import PolymerBrush -from .polymeri import PolymerBrushInteractor from ..mono import FreeInterface, FreeLayer -from .monoi import FreeInterfaceInteractor, FreeLayerInteractor +from ..polymer import PolymerBrush from .layer import NoInteractor, SlabInteractor +from .monoi import FreeInterfaceInteractor, FreeLayerInteractor +from .polymeri import PolymerBrushInteractor # ======================== LayerInteractor factory ==================== diff --git a/refl1d/webview/client/package.json b/refl1d/webview/client/package.json index 6bda749c..b5691081 100644 --- a/refl1d/webview/client/package.json +++ b/refl1d/webview/client/package.json @@ -21,8 +21,7 @@ "plotly.js": "^2.35.2", "socket.io-client": "^4.7.5", "uuid": "^9.0.1", - "vue": "^3.5.4", - "vue-json-viewer": "^3.0.4" + "vue": "^3.5.4" }, "devDependencies": { "@types/plotly.js": "^2.33.3", diff --git a/refl1d/webview/server/api.py b/refl1d/webview/server/api.py index 4cf4b3bb..85eaed66 100644 --- a/refl1d/webview/server/api.py +++ b/refl1d/webview/server/api.py @@ -1,32 +1,32 @@ import asyncio from copy import deepcopy from functools import lru_cache -from typing import Union, Dict, List, TypedDict from pathlib import Path +from typing import Dict, List, Union + +# import bumps.webview.server.api as bumps_api import numpy as np -from refl1d.errors import calc_errors -from refl1d.experiment import Experiment, ExperimentBase, MixedExperiment -import refl1d.probe +from bumps.errplot import error_points_from_state from bumps.webview.server.api import ( - register, + add_notification, get_chisq, - state, - to_json_compatible_dict, log, - now_string, - add_notification, - deserialize_problem, - set_problem, logger, + now_string, + register, + state, + to_json_compatible_dict, ) -import bumps.webview.server.api as bumps_api -from bumps.errplot import error_points_from_state + +import refl1d.probe +from refl1d.errors import calc_errors +from refl1d.experiment import Experiment, ExperimentBase, MixedExperiment + +from .profile_plot import ModelSpec, plot_multiple_sld_profiles # from refl1d.errors import show_errors from .profile_uncertainty import show_errors -from .profile_plot import plot_multiple_sld_profiles, ModelSpec - # state.problem.serializer = "dataclass" diff --git a/refl1d/webview/server/names.py b/refl1d/webview/server/names.py index 650f5fec..28e3bc56 100644 --- a/refl1d/webview/server/names.py +++ b/refl1d/webview/server/names.py @@ -1,2 +1,8 @@ +# from bumps.webview.server.api import state, set_problem, load_session +# from .webserver import main, start_app, create_server_task, sio + + from bumps.webview.server.api import state, set_problem, load_session -from .webserver import main, start_app, create_server_task, sio +from bumps.webview.server.webserver import start_app, create_server_task, sio + +from .webserver import main diff --git a/refl1d/webview/server/profile_plot.py b/refl1d/webview/server/profile_plot.py index 32dc2c79..139dd2ca 100644 --- a/refl1d/webview/server/profile_plot.py +++ b/refl1d/webview/server/profile_plot.py @@ -4,10 +4,12 @@ # Originally written for use in a jupyter notebook # Adapted to act as a prototype for the profile plotting in the new GUI for refl1d -from typing import List, TypedDict, TYPE_CHECKING -from refl1d.experiment import Experiment -from numpy import inf +from typing import TYPE_CHECKING, List, TypedDict + import numpy as np +from numpy import inf + +from refl1d.experiment import Experiment if TYPE_CHECKING: import plotly.graph_objs as go diff --git a/refl1d/webview/server/profile_uncertainty.py b/refl1d/webview/server/profile_uncertainty.py index bde2f2e2..fbe582f7 100644 --- a/refl1d/webview/server/profile_uncertainty.py +++ b/refl1d/webview/server/profile_uncertainty.py @@ -1,10 +1,12 @@ -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING, Optional + import numpy as np if TYPE_CHECKING: import plotly.graph_objs as go -from refl1d.errors import align_profiles, form_quantiles, _find_offset +from refl1d.errors import _find_offset, align_profiles, form_quantiles + from .colors import COLORS ErrorType = tuple[ diff --git a/refl1d/webview/server/webserver.py b/refl1d/webview/server/webserver.py index dcd1ffeb..3308958b 100644 --- a/refl1d/webview/server/webserver.py +++ b/refl1d/webview/server/webserver.py @@ -1,30 +1,17 @@ -import asyncio -from aiohttp import web from dataclasses import dataclass -import json from pathlib import Path -import socket -from typing import Union, Dict, List, Optional -import numpy as np +import bumps.cli from bumps.webview.server import webserver from bumps.webview.server.webserver import ( - start_app, - sio, main, - create_server_task, - display_inline_jupyter, - open_tab_link, ) # Register the refl1d model loader import refl1d.fitplugin -import bumps.cli bumps.cli.install_plugin(refl1d.fitplugin) -from . import api # use side-effects to register refl1d functions -from .profile_plot import plot_sld_profile_plotly webserver.CDN_TEMPLATE = "https://cdn.jsdelivr.net/npm/refl1d-webview-client@{client_version}/dist/{client_version}" webserver.CLIENT_PATH = Path(__file__).parent.parent / "client" diff --git a/release.md b/release.md index d140c235..ec82f4e6 100755 --- a/release.md +++ b/release.md @@ -39,12 +39,15 @@ 1. Build the docs - Even though this should happen automatically in jenkins we should do the builds by hand to make sure they are error free. + This should happen automatically in Jenkins, but we should do the builds by hand to make sure they are error free. ```bash cd doc && make clean html pdf ``` + **Note**: the pdf build requires `latexmk` and `pdflatex` to be installed. + If you don't have these installed, you can omit the pdf build. + 1. Update version number and requirements ```bash diff --git a/tests/mystic/bounds_test.py b/tests/mystic/test_bounds.py similarity index 100% rename from tests/mystic/bounds_test.py rename to tests/mystic/test_bounds.py diff --git a/tests/mystic/condition_test.py b/tests/mystic/test_condition.py similarity index 100% rename from tests/mystic/condition_test.py rename to tests/mystic/test_condition.py diff --git a/tests/mystic/de_test.py b/tests/mystic/test_de.py similarity index 100% rename from tests/mystic/de_test.py rename to tests/mystic/test_de.py diff --git a/tests/mystic/fitservice_test.py b/tests/mystic/test_fitservice.py similarity index 100% rename from tests/mystic/fitservice_test.py rename to tests/mystic/test_fitservice.py diff --git a/tests/mystic/history_test.py b/tests/mystic/test_history.py similarity index 100% rename from tests/mystic/history_test.py rename to tests/mystic/test_history.py diff --git a/tests/mystic/parameter_test.py b/tests/mystic/test_parameter.py similarity index 100% rename from tests/mystic/parameter_test.py rename to tests/mystic/test_parameter.py diff --git a/tests/mystic/stop_test.py b/tests/mystic/test_stop.py similarity index 100% rename from tests/mystic/stop_test.py rename to tests/mystic/test_stop.py diff --git a/tests/refl1d/Q_probe_oversample.py b/tests/refl1d/Q_probe_oversample.py index 3d5adec9..ee561f5c 100644 --- a/tests/refl1d/Q_probe_oversample.py +++ b/tests/refl1d/Q_probe_oversample.py @@ -1,8 +1,7 @@ -import numpy as np import matplotlib.pyplot as plt -import matplotlib.lines as mlines -# %matplotlib notebook +import numpy as np +# %matplotlib notebook import refl1d from refl1d.names import * diff --git a/tests/refl1d/gepore_check.py b/tests/refl1d/gepore_check.py index 7a586ef5..e2a0e500 100644 --- a/tests/refl1d/gepore_check.py +++ b/tests/refl1d/gepore_check.py @@ -1,11 +1,13 @@ -from os.path import join as joinpath, dirname, exists, getmtime as filetime -import tempfile import os +import tempfile +from os.path import dirname, exists +from os.path import getmtime as filetime +from os.path import join as joinpath import numpy as np +from bumps.util import pushdir from numpy import radians -from bumps.util import pushdir from refl1d.reflectivity import magnetic_amplitude as refl H2K = 2.91451e-5 diff --git a/tests/refl1d/surround_variation_gen.py b/tests/refl1d/surround_variation_gen.py index 5ae556a2..46d33f38 100755 --- a/tests/refl1d/surround_variation_gen.py +++ b/tests/refl1d/surround_variation_gen.py @@ -1,8 +1,6 @@ import numpy as np from numpy import sqrt -from numpy.linalg import norm from numpy.random import randn -import os from refl1d.names import * diff --git a/tests/refl1d/align_magnetic_test.py b/tests/refl1d/test_align_magnetic.py similarity index 81% rename from tests/refl1d/align_magnetic_test.py rename to tests/refl1d/test_align_magnetic.py index 3a58cf02..491b93ee 100644 --- a/tests/refl1d/align_magnetic_test.py +++ b/tests/refl1d/test_align_magnetic.py @@ -1,27 +1,24 @@ -from __future__ import division, print_function - import numpy as np -from numpy import inf, nan from refl1d.refllib import backend # thickness, interface, rho, irho -substrate = [[nan, 10, 2, 0.2]] -air = [[nan, nan, 0, 0]] +substrate = [[np.nan, 10, 2, 0.2]] +air = [[np.nan, np.nan, 0, 0]] nuclear_slope = [[2, 2, -0.2 * k + 5, 0] for k in range(5)] # thickness, interface, rhoM, thetaM -magnetic_substrate = [[nan, 10, 1, 270]] -rough_magnetic_substrate = [[nan, 20, 1, 270]] +magnetic_substrate = [[np.nan, 10, 1, 270]] +rough_magnetic_substrate = [[np.nan, 20, 1, 270]] magnetic_slope = [[2, 2, -0.2 * k + 5, 270] for k in range(5)] -magnetic_air = [[nan, nan, 0, 270]] +magnetic_air = [[np.nan, np.nan, 0, 270]] def test_matched_substrate_air(): nuclear = substrate + air magnetic = magnetic_substrate + magnetic_air expected = [ - [nan, 10, 2, 0.2, 1, 270], - [nan, nan, 0, 0, 0, 270], + [np.nan, 10, 2, 0.2, 1, 270], + [np.nan, np.nan, 0, 0, 0, 270], ] check_one(nuclear, magnetic, expected) @@ -30,9 +27,9 @@ def test_unmatched_substrate_air(): nuclear = substrate + air magnetic = rough_magnetic_substrate + magnetic_air expected = [ - [nan, 10, 2, 0.2, 1, 270], + [np.nan, 10, 2, 0.2, 1, 270], [0, 20, 0, 0, 1, 270], - [nan, nan, 0, 0, 0, 270], + [np.nan, np.nan, 0, 0, 0, 270], ] check_one(nuclear, magnetic, expected) @@ -47,13 +44,13 @@ def test_offset(): nuclear = substrate + binder + iron + cap + air magnetic = magnetic_substrate + magnetic_pregap + magnetic_iron + magnetic_postgap + magnetic_air expected = [ - [nan, 10, 2, 0.2, 1, 270], # substrate + [np.nan, 10, 2, 0.2, 1, 270], # substrate [20, 10, 4, 0.4, 0, 270], # binder [5, 5, 8, 0.8, 0, 270], # iron with dead below [40, 5, 8, 0.8, 5, 270], # magnetic iron [5, 10, 8, 0.8, 0, 270], # iron with dead above [10, 10, 6, 0.6, 0, 270], # cap - [nan, nan, 0, 0, 0, 270], # air + [np.nan, np.nan, 0, 0, 0, 270], # air ] check_one(nuclear, magnetic, expected) @@ -62,14 +59,14 @@ def test_stepped_nuclear(): nuclear = substrate + nuclear_slope + air magnetic = magnetic_substrate + [[10, 10, 2, 270]] + magnetic_air expected = [ - [nan, 10, 2, 0.2, 1, 270], + [np.nan, 10, 2, 0.2, 1, 270], [2, 2, 5.0, 0, 2, 270], [2, 2, 4.8, 0, 2, 270], [2, 2, 4.6, 0, 2, 270], [2, 2, 4.4, 0, 2, 270], [2, 2, 4.2, 0, 2, 270], [0, 10, 0, 0, 2, 270], - [nan, nan, 0, 0, 0, 270], + [np.nan, np.nan, 0, 0, 0, 270], ] check_one(nuclear, magnetic, expected) @@ -78,14 +75,14 @@ def test_stepped_magnetic(): nuclear = substrate + [[10, 10, 3, 0.3]] + air magnetic = magnetic_substrate + magnetic_slope + magnetic_air expected = [ - [nan, 10, 2, 0.2, 1, 270], + [np.nan, 10, 2, 0.2, 1, 270], [2, 2, 3, 0.3, 5.0, 270], [2, 2, 3, 0.3, 4.8, 270], [2, 2, 3, 0.3, 4.6, 270], [2, 2, 3, 0.3, 4.4, 270], [2, 10, 3, 0.3, 4.2, 270], [0, 2, 0, 0, 4.2, 270], - [nan, nan, 0, 0, 0, 270], + [np.nan, np.nan, 0, 0, 0, 270], ] check_one(nuclear, magnetic, expected) @@ -104,7 +101,10 @@ def check_one(nuclear, magnetic, expected): for c1, c2 in zip(r1, r2) ) if not good: - nice = lambda v: ", ".join("[" + ", ".join("%g" % c for c in r) + "]" for r in v) + + def nice(v): + return ", ".join("[" + ", ".join("%g" % c for c in r) + "]" for r in v) + raise ValueError("=== Expected:\n%s\n=== Returned:\n%s\n" % (nice(expected), nice(result[:k]))) diff --git a/tests/refl1d/anstodata_test.py b/tests/refl1d/test_anstodata.py similarity index 81% rename from tests/refl1d/anstodata_test.py rename to tests/refl1d/test_anstodata.py index e800e80a..ab4e5b05 100644 --- a/tests/refl1d/anstodata_test.py +++ b/tests/refl1d/test_anstodata.py @@ -1,5 +1,6 @@ import os.path -from numpy.testing import assert_equal, assert_almost_equal + +from numpy.testing import assert_almost_equal, assert_equal from refl1d.anstodata import Platypus diff --git a/tests/refl1d/ncnrdata_test.py b/tests/refl1d/test_ncnrdata.py similarity index 99% rename from tests/refl1d/ncnrdata_test.py rename to tests/refl1d/test_ncnrdata.py index 5bff0a68..11424f7c 100755 --- a/tests/refl1d/ncnrdata_test.py +++ b/tests/refl1d/test_ncnrdata.py @@ -1,4 +1,5 @@ import os + from refl1d import ncnrdata testdir = os.path.dirname(__file__) diff --git a/tests/refl1d/polymer_test.py b/tests/refl1d/test_polymer.py similarity index 99% rename from tests/refl1d/polymer_test.py rename to tests/refl1d/test_polymer.py index b7fcb499..dc98c11f 100644 --- a/tests/refl1d/polymer_test.py +++ b/tests/refl1d/test_polymer.py @@ -9,22 +9,19 @@ haven't changed since that version. """ -from __future__ import division, print_function import numpy as np -from numpy.linalg import norm - from refl1d.names import Material from refl1d.polymer import ( + EndTetheredPolymer, PolymerBrush, PolymerMushroom, - EndTetheredPolymer, - SCFprofile, + Propagator, SCFcache, - SCFsolve, SCFeqns, + SCFprofile, + SCFsolve, SZdist, - Propagator, ) diff --git a/tests/refl1d/resolution_test.py b/tests/refl1d/test_resolution.py similarity index 96% rename from tests/refl1d/resolution_test.py rename to tests/refl1d/test_resolution.py index ae371eab..526bc11d 100755 --- a/tests/refl1d/resolution_test.py +++ b/tests/refl1d/test_resolution.py @@ -1,7 +1,11 @@ -from math import * +from math import asin, degrees, log, pi, radians, sin, sqrt, tan + import numpy as np from numpy.linalg import norm -from refl1d import resolution as res, ncnrdata, snsdata, instrument as inst + +from refl1d import instrument as inst +from refl1d import ncnrdata, snsdata +from refl1d import resolution as res def test(): diff --git a/tests/refl1d/snsdata_test.py b/tests/refl1d/test_snsdata.py similarity index 100% rename from tests/refl1d/snsdata_test.py rename to tests/refl1d/test_snsdata.py diff --git a/tests/refl1d/stack_test.py b/tests/refl1d/test_stack.py similarity index 96% rename from tests/refl1d/stack_test.py rename to tests/refl1d/test_stack.py index 4997fd9a..e0f184b5 100644 --- a/tests/refl1d/stack_test.py +++ b/tests/refl1d/test_stack.py @@ -1,8 +1,10 @@ -from refl1d.names import Slab, Stack, SLD -from copy import deepcopy -from bumps.serialize import serialize, deserialize import pickle +from copy import deepcopy + import dill +from bumps.serialize import deserialize, serialize + +from refl1d.names import SLD, Slab def test_stack_serialization(): diff --git a/tests/refl1d/zeeman_check.py b/tests/refl1d/zeeman_check.py index 456eb741..8363d789 100644 --- a/tests/refl1d/zeeman_check.py +++ b/tests/refl1d/zeeman_check.py @@ -1,12 +1,14 @@ -from os.path import join as joinpath, dirname, exists, getmtime as filetime -import tempfile import os +import tempfile +from os.path import dirname, exists +from os.path import getmtime as filetime +from os.path import join as joinpath +import matplotlib.pyplot as plt import numpy as np +from bumps.util import pushdir from numpy import radians -import matplotlib.pyplot as plt -from bumps.util import pushdir from refl1d.reflectivity import magnetic_amplitude as refl H2K = 2.91451e-5 diff --git a/tests/experiment_test.py b/tests/test_experiment.py similarity index 97% rename from tests/experiment_test.py rename to tests/test_experiment.py index 9df2d407..b5871398 100644 --- a/tests/experiment_test.py +++ b/tests/test_experiment.py @@ -2,12 +2,12 @@ Testing experiment serialization """ -from __future__ import absolute_import, division, print_function -import unittest import os +import unittest + import numpy as np -from refl1d.names import QProbe, Slab, SLD, Parameter, Experiment, NeutronProbe, PolarizedNeutronProbe, Magnetism +from refl1d.names import SLD, Experiment, Magnetism, NeutronProbe, Parameter, PolarizedNeutronProbe, QProbe, Slab class ExperimentJsonTest(unittest.TestCase): From 88fe2fea0af5c0636ebe3810e67ba50d3ad81511 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 20:08:50 +0000 Subject: [PATCH 02/43] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- explore/corrtest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/explore/corrtest.py b/explore/corrtest.py index 27380abc..fd4d2de0 100644 --- a/explore/corrtest.py +++ b/explore/corrtest.py @@ -19,6 +19,7 @@ from refl1d.material import Vacuum + def residual_nllf(v): # Normalize the scores so that we are assuming z = stats.zscore(v) From 1d6fda03816ab85738ead26e53dd90a55b6b4eaa Mon Sep 17 00:00:00 2001 From: glass-ships Date: Fri, 15 Nov 2024 09:30:55 -0500 Subject: [PATCH 03/43] fix tests --- refl1d/support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/refl1d/support.py b/refl1d/support.py index 94bac905..49509e8b 100644 --- a/refl1d/support.py +++ b/refl1d/support.py @@ -24,7 +24,7 @@ def get_data_path(): # Check for data next to the package. try: - root = Path(__file__).resolve().parent.parent.parent + root = Path(__file__).resolve().parent.parent return root / "doc" / "examples" except Exception: raise RuntimeError("Could not find sample data") From 76b7abb924e4088f264f86a660556c3503e107dc Mon Sep 17 00:00:00 2001 From: glass-ships Date: Fri, 15 Nov 2024 11:34:09 -0500 Subject: [PATCH 04/43] initial commit --- refl1d/webview/client/eslint.config.js | 55 ++ refl1d/webview/client/package.json | 31 +- refl1d/webview/client/prettier.config.js | 67 ++ refl1d/webview/client/src/app_state.ts | 4 +- refl1d/webview/client/src/colors.mjs | 14 +- .../client/src/components/DataView.vue | 348 +++++++---- .../client/src/components/ModelView.vue | 81 +-- .../client/src/components/ModelViewPlotly.vue | 141 +++-- .../src/components/ProfileUncertaintyView.vue | 188 +++--- .../client/src/components/SimpleBuilder.vue | 587 +++++++++++------- refl1d/webview/client/src/fitter_defaults.ts | 110 ++-- refl1d/webview/client/src/main.js | 26 +- refl1d/webview/client/src/model.ts | 125 ++-- refl1d/webview/client/src/panels.mjs | 66 +- refl1d/webview/client/src/plot_cache.ts | 2 + refl1d/webview/client/src/plotcache.ts | 2 - .../webview/client/src/standalone_signal.ts | 42 -- 17 files changed, 1152 insertions(+), 737 deletions(-) create mode 100644 refl1d/webview/client/eslint.config.js create mode 100644 refl1d/webview/client/prettier.config.js create mode 100644 refl1d/webview/client/src/plot_cache.ts delete mode 100644 refl1d/webview/client/src/plotcache.ts delete mode 100644 refl1d/webview/client/src/standalone_signal.ts diff --git a/refl1d/webview/client/eslint.config.js b/refl1d/webview/client/eslint.config.js new file mode 100644 index 00000000..5dd38a18 --- /dev/null +++ b/refl1d/webview/client/eslint.config.js @@ -0,0 +1,55 @@ +import url from "url"; +import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; +import pluginVue from "eslint-plugin-vue"; +import { FlatCompat } from "@eslint/eslintrc"; +import js from "@eslint/js"; +import prettierConfig from "@vue/eslint-config-prettier"; +import vueTsEslintConfig from "@vue/eslint-config-typescript"; + +const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + // allConfig: js.configs.all, +}); + +export default [ + /** Extend recommended configs */ + ...compat.extends("plugin:vue/vue3-recommended", "plugin:vuejs-accessibility/recommended", "prettier"), + ...pluginVue.configs["flat/recommended"], + ...vueTsEslintConfig(), + eslintPluginPrettierRecommended, + prettierConfig, + /** Configuration */ + { + languageOptions: { + parserOptions: { + ecmaVersion: "latest", + sourceType: "script", + }, + }, + files: ["src/**/*.js", "src/**/*.mjs", "src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], + /** Override rules */ + rules: { + "max-len": ["error", { code: 120 }], + "prefer-const": 0, + "@typescript-eslint/ban-ts-comment": ["error", { "ts-ignore": "allow-with-description" }], + "@typescript-eslint/no-explicit-any": "off", + "prettier/prettier": [ + "warn", + {}, + { + usePrettierrc: true, + }, + ], + "vuejs-accessibility/label-has-for": [ + "error", + { + required: { + some: ["nesting", "id"], + }, + }, + ], + }, + }, +]; diff --git a/refl1d/webview/client/package.json b/refl1d/webview/client/package.json index b5691081..afdc6a9b 100644 --- a/refl1d/webview/client/package.json +++ b/refl1d/webview/client/package.json @@ -11,22 +11,35 @@ "dev": "vite", "build": "vite build --emptyOutDir -m development", "build_prod": "vite build --emptyOutDir -m production", - "preview": "vite preview --port 4173" + "preview": "vite preview --port 4173", + "lint": "eslint src --fix", + "test:lint": "eslint src" }, "dependencies": { "bootstrap": "^5.3.3", "bumps-webview-client": "^0.1.20", - "comlink": "^4.4.1", + "comlink": "^4.4.2", "json-difference": "^1.16.1", "plotly.js": "^2.35.2", - "socket.io-client": "^4.7.5", - "uuid": "^9.0.1", - "vue": "^3.5.4" + "socket.io-client": "^4.8.1", + "uuid": "^11.0.3", + "vue": "^3.5.13" }, "devDependencies": { - "@types/plotly.js": "^2.33.3", - "@types/uuid": "^8.3.4", - "@vitejs/plugin-vue": "^5.1.3", - "vite": "^5.4.4" + "@types/plotly.js": "^2.35.0", + "@types/uuid": "^10.0.0", + "@vitejs/plugin-vue": "^5.2.0", + "@vue/eslint-config-prettier": "10.1.0", + "@vue/eslint-config-typescript": "^14.1.3", + "vite": "^5.4.11", + "eslint": "^9.14.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-vue": "^9.31.0", + "eslint-plugin-vuejs-accessibility": "^2.4.1", + "prettier": "^3.3.3", + "prettier-plugin-css-order": "^2.1.2", + "prettier-plugin-jsdoc": "^1.3.0", + "@ianvs/prettier-plugin-sort-imports": "^4.4.0" } } diff --git a/refl1d/webview/client/prettier.config.js b/refl1d/webview/client/prettier.config.js new file mode 100644 index 00000000..47565cd2 --- /dev/null +++ b/refl1d/webview/client/prettier.config.js @@ -0,0 +1,67 @@ +/** + * @see https://prettier.io/docs/en/configuration.html + * @type {import("prettier").Config} + */ + +// module.exports ={ +// "plugins": [ +// "@ianvs/prettier-plugin-sort-imports", +// "./node_modules/prettier-plugin-jsdoc/dist/index.js", +// "prettier-plugin-css-order" +// ], +// "importOrder": ["^vue", "^[a-zA-Z]", "^@[a-zA-Z]", "^@/", "^./", "^../"], +// "cssDeclarationSorterOrder": "smacss", +// "jsdocCapitalizeDescription": false, +// "overrides": [ +// { +// "files": "*.svg", +// "options": { +// "parser": "html" +// } +// } +// ] +// } + +const config = { + "plugins": [ + "@ianvs/prettier-plugin-sort-imports", + "./node_modules/prettier-plugin-jsdoc/dist/index.js", + "prettier-plugin-css-order" + ], + "importOrder": ["^vue", "^[a-zA-Z]", "^@[a-zA-Z]", "^@/", "^./", "^../"], + "cssDeclarationSorterOrder": "smacss", + "jsdocCapitalizeDescription": false, + "overrides": [ + { + "files": "*.svg", + "options": { + "parser": "html" + } + } + ], + experimentalTernaries: true, + printWidth: 120, + // tabWidth: 4, + // useTabs: false, + // semi: true, + // singleQuote: false, + // jsxSingleQuote: false, + // quoteProps: "as-needed", + trailingComma: "es5", + // bracketSpacing: true, + // bracketSameLine: false, + // arrowParens: "always", + // rangeStart: 0, + // rangeEnd: Infinity, + // parser: "babel", + // requirePragma: false, + // insertPragma: false, + // proseWrap: "preserve", + // htmlWhitespaceSensitivity: "css", + // vueIndentScriptAndStyle: false, + // endOfLine: "lf", + // embeddedLanguageFormatting: "auto", + // singleAttributePerLine: true, +}; + +export default config; diff --git a/refl1d/webview/client/src/app_state.ts b/refl1d/webview/client/src/app_state.ts index 0fbce66e..a84151bd 100644 --- a/refl1d/webview/client/src/app_state.ts +++ b/refl1d/webview/client/src/app_state.ts @@ -1,4 +1,4 @@ -import { ref, shallowRef } from 'vue'; +import { ref } from "vue"; // Whether data files to be loaded have their dQ as standard deviation or FWHM -export const dq_is_FWHM = ref(false); \ No newline at end of file +export const dqIsFWHM = ref(false); diff --git a/refl1d/webview/client/src/colors.mjs b/refl1d/webview/client/src/colors.mjs index 27535d17..b7577369 100644 --- a/refl1d/webview/client/src/colors.mjs +++ b/refl1d/webview/client/src/colors.mjs @@ -1,6 +1,12 @@ // Colorblind-friendly colors, as per https://gist.github.com/thriveth/8560036 export const COLORS = [ - '#377eb8', '#ff7f00', '#4daf4a', - '#f781bf', '#a65628', '#984ea3', - '#999999', '#e41a1c', '#dede00' -]; \ No newline at end of file + "#377eb8", + "#ff7f00", + "#4daf4a", + "#f781bf", + "#a65628", + "#984ea3", + "#999999", + "#e41a1c", + "#dede00", +]; diff --git a/refl1d/webview/client/src/components/DataView.vue b/refl1d/webview/client/src/components/DataView.vue index 43a43bdd..5e28d40f 100644 --- a/refl1d/webview/client/src/components/DataView.vue +++ b/refl1d/webview/client/src/components/DataView.vue @@ -1,13 +1,13 @@ @@ -407,4 +511,4 @@ function interp(x: number[], xp: number[], fp: number[]): number[] { .plot-mode { width: 100%; } - \ No newline at end of file + diff --git a/refl1d/webview/client/src/components/ModelView.vue b/refl1d/webview/client/src/components/ModelView.vue index 142a4437..3e325a5c 100644 --- a/refl1d/webview/client/src/components/ModelView.vue +++ b/refl1d/webview/client/src/components/ModelView.vue @@ -1,90 +1,93 @@ diff --git a/refl1d/webview/client/src/components/ModelViewPlotly.vue b/refl1d/webview/client/src/components/ModelViewPlotly.vue index 6291994b..00dfdfe3 100644 --- a/refl1d/webview/client/src/components/ModelViewPlotly.vue +++ b/refl1d/webview/client/src/components/ModelViewPlotly.vue @@ -1,30 +1,30 @@ - + diff --git a/refl1d/webview/client/src/components/ProfileUncertaintyView.vue b/refl1d/webview/client/src/components/ProfileUncertaintyView.vue index eebc9a92..4826a133 100644 --- a/refl1d/webview/client/src/components/ProfileUncertaintyView.vue +++ b/refl1d/webview/client/src/components/ProfileUncertaintyView.vue @@ -1,18 +1,17 @@ - + diff --git a/refl1d/webview/server/api.py b/refl1d/webview/server/api.py index 85eaed66..24d36c53 100644 --- a/refl1d/webview/server/api.py +++ b/refl1d/webview/server/api.py @@ -44,7 +44,8 @@ async def get_plot_data(view: str = "linear"): assert isinstance(model, ExperimentBase) theory = model.reflectivity() probe = model.probe - plotdata.append(get_probe_data(theory, probe, model._substrate, model._surface)) + probe_data = get_probe_data(theory, probe, model._substrate, model._surface) + plotdata.append(probe_data) return to_json_compatible_dict(result) diff --git a/refl1d/webview/server/webserver.py b/refl1d/webview/server/webserver.py index 3308958b..fb1a808c 100644 --- a/refl1d/webview/server/webserver.py +++ b/refl1d/webview/server/webserver.py @@ -1,17 +1,31 @@ +# import asyncio +# from aiohttp import web from dataclasses import dataclass + +# import json from pathlib import Path +# import socket +# from typing import Union, Dict, List, Optional -import bumps.cli +# import numpy as np from bumps.webview.server import webserver from bumps.webview.server.webserver import ( + # start_app, + # sio, main, + # create_server_task, + # display_inline_jupyter, + # open_tab_link, ) # Register the refl1d model loader import refl1d.fitplugin +import bumps.cli bumps.cli.install_plugin(refl1d.fitplugin) +from . import api # use side-effects to register refl1d functions +from .profile_plot import plot_sld_profile_plotly webserver.CDN_TEMPLATE = "https://cdn.jsdelivr.net/npm/refl1d-webview-client@{client_version}/dist/{client_version}" webserver.CLIENT_PATH = Path(__file__).parent.parent / "client" From fd35c6aae94e0ee9414c68d619fe589b5e3ddd3c Mon Sep 17 00:00:00 2001 From: glass-ships Date: Fri, 15 Nov 2024 13:07:33 -0500 Subject: [PATCH 06/43] Add label for plot mode selection --- Makefile | 39 ++++++++++++++++++- .../client/src/components/DataView.vue | 21 +++++----- refl1d/webview/client/src/main.js | 4 +- refl1d/webview/client/src/model.ts | 4 +- 4 files changed, 52 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 5b095bbf..bf1ebcde 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,15 @@ ROOTDIR = $(shell pwd) +# Check for the presence of bun or npm +ifneq ($(shell which bun),) + FE_CMD=bun +else ifneq ($(shell which npm),) + FE_CMD=npm +else + echo "No frontend build tool found. Please install 'bun' or 'npm'." + exit 1 +endif + # This nifty perl one-liner collects all comments headed by the double "#" symbols next to each target and recycles them as comments .PHONY: help help: ## Print this help message @@ -20,10 +30,35 @@ test: ## Run pytest and doc tests pytest -v python check_examples.py --chisq +############################## +### Linting and formatting ### +############################## + .PHONY: lint -lint: ## Run ruff linting +lint: lint-backend lint-frontend ## Run all linters + +.PHONY: lint-backend +lint-backend: ## Run ruff linting on python code @ruff check --fix refl1d/ tests/ setup.py +.PHONY: lint-frontend-check +lint-frontend-check: ## Run bun linting check on javascript code + cd refl1d/webview/client && \ + $(FE_CMD) run test:lint + +.PHONY: lint-frontend +lint-frontend: ## Run bun linting fix on javascript code + cd refl1d/webview/client && \ + $(FE_CMD) run lint + .PHONY: format -format: ## Run ruff formatting +format: format-backend format-frontend ## Run all formatters + +.PHONY: format-backend +format-backend: ## Run ruff formatting on python code @ruff format refl1d/ tests/ setup.py + +.PHONY: format-frontend +format-frontend: ## Run bun formatting on javascript code + cd refl1d/webview/client && \ + $(FE_CMD) run lint \ No newline at end of file diff --git a/refl1d/webview/client/src/components/DataView.vue b/refl1d/webview/client/src/components/DataView.vue index 3347679f..3a646400 100644 --- a/refl1d/webview/client/src/components/DataView.vue +++ b/refl1d/webview/client/src/components/DataView.vue @@ -453,7 +453,8 @@ function interp(x: number[], xp: number[], fp: number[]): number[] {
- @@ -461,28 +462,28 @@ function interp(x: number[], xp: number[], fp: number[]): number[] {
- +
- +
@@ -494,18 +495,18 @@ function interp(x: number[], xp: number[], fp: number[]): number[] {
-
+
diff --git a/refl1d/webview/client/src/main.js b/refl1d/webview/client/src/main.js index 00bac3a2..d76056b2 100644 --- a/refl1d/webview/client/src/main.js +++ b/refl1d/webview/client/src/main.js @@ -1,7 +1,7 @@ import "bootstrap/dist/css/bootstrap.min.css"; -import App from "bumps-webview-client/src/App.vue"; -import { fileBrowser, menu_items, model_file, socket } from "bumps-webview-client/src/app_state"; import { computed, createApp } from "vue"; +import { fileBrowser, menu_items, model_file, socket } from "bumps-webview-client/src/app_state"; +import App from "bumps-webview-client/src/App.vue"; import { dqIsFWHM } from "./app_state"; import { panels } from "./panels.mjs"; import "./style.css"; diff --git a/refl1d/webview/client/src/model.ts b/refl1d/webview/client/src/model.ts index 436fe170..a94511cf 100644 --- a/refl1d/webview/client/src/model.ts +++ b/refl1d/webview/client/src/model.ts @@ -1,6 +1,6 @@ /** - * This file contains the types for the serialized model format used by the refl1d webapp. - * ! Note that the fields must match the Python model serialization format, and must therefore be snake_case + * This file contains the types for the serialized model format used by the refl1d webapp. Note that the fields must + * match the Python model serialization format, and must therefore be snake_case */ export interface SerializedModel { From 9c77d265ed082954daa67aed2eb647710ddf9eb5 Mon Sep 17 00:00:00 2001 From: glass-ships Date: Fri, 15 Nov 2024 13:09:40 -0500 Subject: [PATCH 07/43] add github action to test frontend --- .github/dependabot.yaml | 9 +++ .github/workflows/test-webview-client.yaml | 91 ++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 .github/dependabot.yaml create mode 100644 .github/workflows/test-webview-client.yaml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 00000000..0f96f8de --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,9 @@ +# Set update schedule for GitHub Actions + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every week + interval: "weekly" diff --git a/.github/workflows/test-webview-client.yaml b/.github/workflows/test-webview-client.yaml new file mode 100644 index 00000000..65da1b7f --- /dev/null +++ b/.github/workflows/test-webview-client.yaml @@ -0,0 +1,91 @@ +name: Test Webview Client + +on: pull_request + +defaults: + run: + working-directory: ./refl1d/webview/client + +jobs: + # test that app can build without issues + test-build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Bun + uses: oven-sh/setup-bun@v2 + + - name: Install packages + run: bun install + + - name: Run test + run: bun run build + + # test that app has no typescript errors +# test-types: +# runs-on: ubuntu-latest +# steps: +# - name: Checkout code +# uses: actions/checkout@v4 + +# - name: Set up Bun +# uses: oven-sh/setup-bun@v2 + +# - name: Install packages +# run: bun install + +# - name: Run test +# run: bun run test:types + + # test that app is properly formatted and linted + test-lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Bun + uses: oven-sh/setup-bun@v2 + + - name: Install packages + run: bun install + + - name: Run test + run: bun run test:lint + + # run unit tests +# test-unit: +# runs-on: ubuntu-latest +# steps: +# - name: Checkout code +# uses: actions/checkout@v4 + +# - name: Set up Bun +# uses: oven-sh/setup-bun@v2 + +# - name: Install packages +# run: bun install + +# - name: Run test +# run: bun run test:unit + + # run end to end integration tests +# test-e2e: +# runs-on: ubuntu-latest +# steps: +# - name: Checkout code +# uses: actions/checkout@v4 + +# - name: Set up Bun +# uses: oven-sh/setup-bun@v2 + +# - name: Install packages +# run: bun install + +# - name: Install Playwright +# run: bunx playwright install + +# - name: Run test +# run: bun run test:e2e From 8e76ffdb1afdccde156ceb38415ad2702c1a2997 Mon Sep 17 00:00:00 2001 From: glass-ships Date: Fri, 15 Nov 2024 14:05:12 -0500 Subject: [PATCH 08/43] downgrade eslint until a fix is released --- Makefile | 2 +- refl1d/webview/client/eslint.config.js | 6 +++--- refl1d/webview/client/package.json | 8 ++++++-- refl1d/webview/client/src/style.css | 10 ++++++---- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index bf1ebcde..7c4dc8ec 100644 --- a/Makefile +++ b/Makefile @@ -61,4 +61,4 @@ format-backend: ## Run ruff formatting on python code .PHONY: format-frontend format-frontend: ## Run bun formatting on javascript code cd refl1d/webview/client && \ - $(FE_CMD) run lint \ No newline at end of file + $(FE_CMD) run format \ No newline at end of file diff --git a/refl1d/webview/client/eslint.config.js b/refl1d/webview/client/eslint.config.js index 5dd38a18..03b71f26 100644 --- a/refl1d/webview/client/eslint.config.js +++ b/refl1d/webview/client/eslint.config.js @@ -1,10 +1,10 @@ -import url from "url"; -import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; -import pluginVue from "eslint-plugin-vue"; import { FlatCompat } from "@eslint/eslintrc"; import js from "@eslint/js"; import prettierConfig from "@vue/eslint-config-prettier"; import vueTsEslintConfig from "@vue/eslint-config-typescript"; +import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; +import pluginVue from "eslint-plugin-vue"; +import url from "url"; const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); const compat = new FlatCompat({ diff --git a/refl1d/webview/client/package.json b/refl1d/webview/client/package.json index afdc6a9b..f244644d 100644 --- a/refl1d/webview/client/package.json +++ b/refl1d/webview/client/package.json @@ -12,6 +12,7 @@ "build": "vite build --emptyOutDir -m development", "build_prod": "vite build --emptyOutDir -m production", "preview": "vite preview --port 4173", + "format": "prettier --write src", "lint": "eslint src --fix", "test:lint": "eslint src" }, @@ -32,7 +33,7 @@ "@vue/eslint-config-prettier": "10.1.0", "@vue/eslint-config-typescript": "^14.1.3", "vite": "^5.4.11", - "eslint": "^9.14.0", + "eslint": "9.14.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-vue": "^9.31.0", @@ -41,5 +42,8 @@ "prettier-plugin-css-order": "^2.1.2", "prettier-plugin-jsdoc": "^1.3.0", "@ianvs/prettier-plugin-sort-imports": "^4.4.0" - } + }, + "trustedDependencies": [ + "es5-ext" + ] } diff --git a/refl1d/webview/client/src/style.css b/refl1d/webview/client/src/style.css index a2b25da4..89fe70cc 100644 --- a/refl1d/webview/client/src/style.css +++ b/refl1d/webview/client/src/style.css @@ -1,7 +1,9 @@ -html, body, #app { - height: 100%; +html, +body, +#app { + height: 100%; } .flex-column { - min-height: 0; -} \ No newline at end of file + min-height: 0; +} From f11b69328cbf9d09c36a26463291b1a68e5b02fa Mon Sep 17 00:00:00 2001 From: glass-ships Date: Fri, 15 Nov 2024 14:09:41 -0500 Subject: [PATCH 09/43] add pre-commit hook for linting frontend --- .pre-commit-config.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 65b9c26f..2d39a0a2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,10 @@ repos: + - repo: local + hooks: + - id: run-eslint + name: run-eslint + entry: bash -c "make lint-frontend" + language: system - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: @@ -18,4 +24,4 @@ repos: - id: ruff-format ci: - autoupdate_schedule: monthly \ No newline at end of file + autoupdate_schedule: monthly From 3a3415a29e59d1e0e06ffc71c237c653f20b8a90 Mon Sep 17 00:00:00 2001 From: glass-ships Date: Fri, 15 Nov 2024 14:11:30 -0500 Subject: [PATCH 10/43] fix makefile? --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 7c4dc8ec..fb79915a 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,12 @@ ROOTDIR = $(shell pwd) # Check for the presence of bun or npm ifneq ($(shell which bun),) - FE_CMD=bun + FE_CMD=bun else ifneq ($(shell which npm),) - FE_CMD=npm + FE_CMD=npm else - echo "No frontend build tool found. Please install 'bun' or 'npm'." - exit 1 + echo "No frontend build tool found. Please install 'bun' or 'npm'." + exit 1 endif # This nifty perl one-liner collects all comments headed by the double "#" symbols next to each target and recycles them as comments From 62b0f71cc1b6de52d4fb9f48b31984c40deb14bd Mon Sep 17 00:00:00 2001 From: glass-ships Date: Fri, 15 Nov 2024 14:14:43 -0500 Subject: [PATCH 11/43] pre-commit ci doesn't seem to like using make target --- .github/workflows/test-webview-client.yaml | 32 +++++++++++----------- .pre-commit-config.yaml | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test-webview-client.yaml b/.github/workflows/test-webview-client.yaml index 65da1b7f..5125b0db 100644 --- a/.github/workflows/test-webview-client.yaml +++ b/.github/workflows/test-webview-client.yaml @@ -23,22 +23,6 @@ jobs: - name: Run test run: bun run build - # test that app has no typescript errors -# test-types: -# runs-on: ubuntu-latest -# steps: -# - name: Checkout code -# uses: actions/checkout@v4 - -# - name: Set up Bun -# uses: oven-sh/setup-bun@v2 - -# - name: Install packages -# run: bun install - -# - name: Run test -# run: bun run test:types - # test that app is properly formatted and linted test-lint: runs-on: ubuntu-latest @@ -55,6 +39,22 @@ jobs: - name: Run test run: bun run test:lint + # test that app has no typescript errors +# test-types: +# runs-on: ubuntu-latest +# steps: +# - name: Checkout code +# uses: actions/checkout@v4 + +# - name: Set up Bun +# uses: oven-sh/setup-bun@v2 + +# - name: Install packages +# run: bun install + +# - name: Run test +# run: bun run test:types + # run unit tests # test-unit: # runs-on: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2d39a0a2..3c4480ee 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ repos: hooks: - id: run-eslint name: run-eslint - entry: bash -c "make lint-frontend" + entry: bash -c "cd refl1d/webview/client && npm run lint" language: system - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 From 8306d17a523818ee04083c8ee179b2c5283e780c Mon Sep 17 00:00:00 2001 From: glass-ships Date: Fri, 15 Nov 2024 14:18:59 -0500 Subject: [PATCH 12/43] pre-commit ci will take more effort. github action will suffice for now --- .pre-commit-config.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3c4480ee..466b3848 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,10 @@ repos: - - repo: local - hooks: - - id: run-eslint - name: run-eslint - entry: bash -c "cd refl1d/webview/client && npm run lint" - language: system + # - repo: local + # hooks: + # - id: run-eslint + # name: run-eslint + # entry: bash -c "cd refl1d/webview/client && npm run lint" + # language: system - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: From ac9fa53d758a095c8a3508eb72d24cdc84dadcca Mon Sep 17 00:00:00 2001 From: glass-ships Date: Mon, 18 Nov 2024 13:54:12 -0500 Subject: [PATCH 13/43] Add type testing for frontend --- .github/workflows/test-webview-client.yaml | 22 ++++++++--------- refl1d/webview/client/eslint.config.js | 10 +++++--- refl1d/webview/client/package.json | 8 +++++-- .../client/src/{colors.mjs => colors.ts} | 0 .../client/src/components/DataView.vue | 6 ++--- .../client/src/components/ModelView.vue | 2 +- .../client/src/components/ModelViewPlotly.vue | 2 +- .../src/components/ProfileUncertaintyView.vue | 2 +- .../client/src/components/SimpleBuilder.vue | 13 ++++++---- refl1d/webview/client/src/main.js | 2 +- refl1d/webview/client/src/model.ts | 4 ++-- .../client/src/{panels.mjs => panels.ts} | 13 +++++++--- refl1d/webview/client/tsconfig.json | 24 +++++++++++++++++++ 13 files changed, 75 insertions(+), 33 deletions(-) rename refl1d/webview/client/src/{colors.mjs => colors.ts} (100%) rename refl1d/webview/client/src/{panels.mjs => panels.ts} (84%) create mode 100644 refl1d/webview/client/tsconfig.json diff --git a/.github/workflows/test-webview-client.yaml b/.github/workflows/test-webview-client.yaml index 5125b0db..7ce8c17f 100644 --- a/.github/workflows/test-webview-client.yaml +++ b/.github/workflows/test-webview-client.yaml @@ -40,20 +40,20 @@ jobs: run: bun run test:lint # test that app has no typescript errors -# test-types: -# runs-on: ubuntu-latest -# steps: -# - name: Checkout code -# uses: actions/checkout@v4 + test-types: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 -# - name: Set up Bun -# uses: oven-sh/setup-bun@v2 + - name: Set up Bun + uses: oven-sh/setup-bun@v2 -# - name: Install packages -# run: bun install + - name: Install packages + run: bun install -# - name: Run test -# run: bun run test:types + - name: Run test + run: bun run test:types # run unit tests # test-unit: diff --git a/refl1d/webview/client/eslint.config.js b/refl1d/webview/client/eslint.config.js index 03b71f26..5e5d8e79 100644 --- a/refl1d/webview/client/eslint.config.js +++ b/refl1d/webview/client/eslint.config.js @@ -1,10 +1,10 @@ +import url from "url"; +import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; +import pluginVue from "eslint-plugin-vue"; import { FlatCompat } from "@eslint/eslintrc"; import js from "@eslint/js"; import prettierConfig from "@vue/eslint-config-prettier"; import vueTsEslintConfig from "@vue/eslint-config-typescript"; -import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; -import pluginVue from "eslint-plugin-vue"; -import url from "url"; const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); const compat = new FlatCompat({ @@ -35,6 +35,10 @@ export default [ "prefer-const": 0, "@typescript-eslint/ban-ts-comment": ["error", { "ts-ignore": "allow-with-description" }], "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-expressions": [ + "error", + { allowShortCircuit: true, allowTernary: true }, // Temporary fix for indirect dependency @typescript-eslint <= 8.15.0 + ], "prettier/prettier": [ "warn", {}, diff --git a/refl1d/webview/client/package.json b/refl1d/webview/client/package.json index f244644d..d7d364ae 100644 --- a/refl1d/webview/client/package.json +++ b/refl1d/webview/client/package.json @@ -14,7 +14,8 @@ "preview": "vite preview --port 4173", "format": "prettier --write src", "lint": "eslint src --fix", - "test:lint": "eslint src" + "test:lint": "eslint src", + "test:types": "vue-tsc --noEmit --composite false" }, "dependencies": { "bootstrap": "^5.3.3", @@ -30,10 +31,13 @@ "@types/plotly.js": "^2.35.0", "@types/uuid": "^10.0.0", "@vitejs/plugin-vue": "^5.2.0", + "@tsconfig/node22": "22.0.0", + "@vue/tsconfig": "^0.6.0", "@vue/eslint-config-prettier": "10.1.0", "@vue/eslint-config-typescript": "^14.1.3", "vite": "^5.4.11", - "eslint": "9.14.0", + "vue-tsc": "^2.1.10", + "eslint": "^9.15.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-vue": "^9.31.0", diff --git a/refl1d/webview/client/src/colors.mjs b/refl1d/webview/client/src/colors.ts similarity index 100% rename from refl1d/webview/client/src/colors.mjs rename to refl1d/webview/client/src/colors.ts diff --git a/refl1d/webview/client/src/components/DataView.vue b/refl1d/webview/client/src/components/DataView.vue index 3a646400..83168a07 100644 --- a/refl1d/webview/client/src/components/DataView.vue +++ b/refl1d/webview/client/src/components/DataView.vue @@ -1,11 +1,11 @@