diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 032bfc9..386e6b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,23 +6,6 @@ on: jobs: - static-analysis: - name: Perform static analysis - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Set up Pixi - uses: prefix-dev/setup-pixi@v0.8.1 - with: - pixi-version: v0.40.2 - cache: false - environments: fmt - activate-environment: true - - name: Run formatter and linter - run: pixi run fmt - docs: name: Generate documentation runs-on: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..3f890af --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.12.0 + hooks: + # Run the linter. + - id: ruff-check + args: [ --fix ] + # Run the formatter. + - id: ruff-format diff --git a/pyproject.toml b/pyproject.toml index 82b8e22..3d6dfd8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,9 +44,6 @@ platforms = ["linux-64"] ### pixi: features [tool.pixi.feature.py3] dependencies = {python = "3.*"} -[tool.pixi.feature.ruff] -dependencies = {ruff = "*"} -tasks = {fmt = "ruff check"} [tool.pixi.feature.build] pypi-dependencies = {build = "*"} tasks = {build-dist = "python -m build"} @@ -63,12 +60,12 @@ dependencies = {fans = "0.4.*"} [tool.pixi.dependencies] aiida-core = "2.6.*" h5py = "*" +pre-commit = "*" [tool.pixi.pypi-dependencies] aiida-fans = { path = ".", editable = true } ### pixi: environments [tool.pixi.environments] -fmt = { no-default-feature = true, features = ["py3", "ruff"] } # CI env dist = { no-default-feature = true, features = ["py3", "build"] } # CI env docs = { no-default-feature = true, features = ["py3", "sphinx"] } # CI env tutorial = { features = ["marimo", "fans"] } diff --git a/src/aiida_fans/calculations.py b/src/aiida_fans/calculations.py index 65178d1..8b6311e 100644 --- a/src/aiida_fans/calculations.py +++ b/src/aiida_fans/calculations.py @@ -26,10 +26,7 @@ def define(cls, spec: CalcJobProcessSpec) -> None: spec.inputs["metadata"]["label"].default = "FANS" ## Processing Power spec.inputs["metadata"]["options"]["withmpi"].default = True - spec.inputs["metadata"]["options"]["resources"].default = { - "num_machines": 1, - "num_mpiprocs_per_machine": 4 - } + spec.inputs["metadata"]["options"]["resources"].default = {"num_machines": 1, "num_mpiprocs_per_machine": 4} ## Filenames spec.inputs["metadata"]["options"]["input_filename"].default = "input.json" spec.inputs["metadata"]["options"]["output_filename"].default = "output.h5" @@ -72,14 +69,16 @@ def prepare_for_submission(self, folder: Folder) -> CalcInfo: """Prepare the calculation for submission.""" # Stashed Strategy: if self.options.stashed_microstructure: - ms_filepath: Path = Path(self.inputs.code.computer.get_workdir()) / \ - "stash/microstructures" / \ - self.inputs.microstructure.file.filename + ms_filepath: Path = ( + Path(self.inputs.code.computer.get_workdir()) + / "stash/microstructures" + / self.inputs.microstructure.file.filename + ) # if microstructure does not exist in stash, make it if not ms_filepath.is_file(): ms_filepath.parent.mkdir(parents=True, exist_ok=True) - with self.inputs.microstructure.file.open(mode='rb') as source: - with ms_filepath.open(mode='wb') as target: + with self.inputs.microstructure.file.open(mode="rb") as source: + with ms_filepath.open(mode="wb") as target: copyfileobj(source, target) # input.json as dict @@ -90,11 +89,11 @@ def prepare_for_submission(self, folder: Folder) -> CalcInfo: dump(input_dict, json, indent=4) # Fragmented Strategy: else: - datasetname : str = self.inputs.microstructure.datasetname.value - with folder.open("microstructure.h5","bw") as f_dest: - with h5File(f_dest,"w") as h5_dest: + datasetname: str = self.inputs.microstructure.datasetname.value + with folder.open("microstructure.h5", "bw") as f_dest: + with h5File(f_dest, "w") as h5_dest: with self.inputs.microstructure.file.open(mode="rb") as f_src: - with h5File(f_src,'r') as h5_src: + with h5File(f_src, "r") as h5_src: h5_src.copy(datasetname, h5_dest, name=datasetname) # input.json as dict @@ -117,8 +116,6 @@ def prepare_for_submission(self, folder: Folder) -> CalcInfo: calcinfo.local_copy_list = [] calcinfo.remote_copy_list = [] calcinfo.retrieve_list = [codeinfo.stdout_name, codeinfo.stderr_name] - calcinfo.retrieve_temporary_list = [ - self.options.output_filename - ] + calcinfo.retrieve_temporary_list = [self.options.output_filename] return calcinfo diff --git a/src/aiida_fans/helpers.py b/src/aiida_fans/helpers.py index f527c54..e5cbd79 100644 --- a/src/aiida_fans/helpers.py +++ b/src/aiida_fans/helpers.py @@ -11,9 +11,9 @@ def make_input_dict(job: CalcJob) -> dict[str, Any]: return { ## Microstructure Definition "microstructure": { - "filepath": None, # path to stashed microstructure, must be overwritten by impl + "filepath": None, # path to stashed microstructure, must be overwritten by impl "datasetname": job.inputs.microstructure.datasetname.value, - "L": job.inputs.microstructure.L.get_list() + "L": job.inputs.microstructure.L.get_list(), }, "results_prefix": job.inputs.metadata.options.results_prefix, ## Problem Type and Material Model @@ -26,14 +26,15 @@ def make_input_dict(job: CalcJob) -> dict[str, Any]: "error_parameters": { "measure": job.inputs.error_parameters.measure.value, "type": job.inputs.error_parameters.type.value, - "tolerance": job.inputs.error_parameters.tolerance.value + "tolerance": job.inputs.error_parameters.tolerance.value, }, ## Macroscale Loading Conditions "macroscale_loading": job.inputs.macroscale_loading.get_list(), ## Results Specification - "results": job.inputs.metadata.options.results + "results": job.inputs.metadata.options.results, } + def arraydata_equal(first: dict[str, ndarray], second: dict[str, ndarray]) -> bool: """Return whether two dicts of arrays are roughly equal.""" if first.keys() != second.keys(): diff --git a/src/aiida_fans/parsers.py b/src/aiida_fans/parsers.py index f755ba8..f22b21e 100644 --- a/src/aiida_fans/parsers.py +++ b/src/aiida_fans/parsers.py @@ -19,7 +19,7 @@ def __init__(self, node: CalcJobNode): def parse(self, **kwargs) -> ExitCode | None: """Parse outputs and store results as nodes.""" - output_path: Path = Path(kwargs["retrieved_temporary_folder"]) / self.node.get_option("output_filename") # type: ignore + output_path: Path = Path(kwargs["retrieved_temporary_folder"]) / self.node.get_option("output_filename") # type: ignore if output_path.is_file(): self.out("output", node=SinglefileData(output_path)) else: @@ -27,9 +27,7 @@ def parse(self, **kwargs) -> ExitCode | None: with h5File(output_path) as h5: results = h5[ - self.node.inputs.microstructure.datasetname.value + \ - "_results/" + \ - self.node.get_option('results_prefix') + self.node.inputs.microstructure.datasetname.value + "_results/" + self.node.get_option("results_prefix") ] results.visititems(self.parse_h5) diff --git a/src/aiida_fans/utils.py b/src/aiida_fans/utils.py index 2da1ccf..ba9e1ac 100644 --- a/src/aiida_fans/utils.py +++ b/src/aiida_fans/utils.py @@ -132,10 +132,7 @@ def compile_query(ins: dict[str, Any], qb: QueryBuilder) -> None: qb.append(cls=type(v), with_outgoing="calc", filters={"pk": v.pk}) -def execute_fans( - mode: Literal["Submit", "Run"], - inputs: dict[str, Any] -): +def execute_fans(mode: Literal["Submit", "Run"], inputs: dict[str, Any]): """This utility function simplifies the process of executing aiida-fans jobs. The only nodes you must provide are the `code` and `microstructure` inputs. @@ -199,15 +196,11 @@ def execute_fans( submit(calcjob, inputs) # type: ignore -def submit_fans( - inputs: dict[str, Any] -): +def submit_fans(inputs: dict[str, Any]): """See `execute_fans` for implementation and usage details.""" execute_fans("Submit", inputs) -def run_fans( - inputs: dict[str, Any] -): +def run_fans(inputs: dict[str, Any]): """See `execute_fans` for implementation and usage details.""" execute_fans("Run", inputs)