From a650fa785f6fa9ee1993ac73c1d92e1abd2303f8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 20:00:36 +0000 Subject: [PATCH 1/2] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.16.4 → v0.16.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.16.4...v0.16.5) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4f4029da0..81c63d511 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.16.4 + rev: v0.16.5 hooks: - id: ruff name: ruff lint From 297ab31f587deccd79029ebd9de3bcc3ef28f103 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 20:01:10 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- README.md | 172 +++++++++++++++++++++---------------------- docs/developer.md | 10 +-- docs/example.md | 43 +++++------ docs/installation.md | 2 + docs/tutorial.md | 12 +-- specs/job_type.md | 9 ++- 6 files changed, 124 insertions(+), 124 deletions(-) diff --git a/README.md b/README.md index 2f6b069c6..f0337d47f 100644 --- a/README.md +++ b/README.md @@ -51,93 +51,91 @@ implementation of the [quantum espresso](https://www.quantum-espresso.org) densi code in the `pyiron_base` workflow manager is chosen as example. Still the same steps apply for any kind of simulation code: ```python - import os - import matplotlib.pyplot as plt - import numpy as np - from ase.build import bulk - from ase.calculators.espresso import Espresso - from ase.io import write - from pwtools import io - - - def write_input(input_dict, working_directory="."): - filename = os.path.join(working_directory, 'input.pwi') - os.makedirs(working_directory, exist_ok=True) - write( - filename=filename, - images=input_dict["structure"], - Crystal=True, - kpts=input_dict["kpts"], - input_data={"calculation": input_dict["calculation"]}, - pseudopotentials=input_dict["pseudopotentials"], - tstress=True, - tprnfor=True - ) - - - def collect_output(working_directory="."): - filename = os.path.join(working_directory, 'output.pwo') - try: - return {"structure": io.read_pw_md(filename)[-1].get_ase_atoms()} - except TypeError: - out = io.read_pw_scf(filename) - return { - "energy": out.etot, - "volume": out.volume, - } - - - def workflow(project, structure): - # Structure optimization - job_qe_minimize = pr.create.job.QEJob(job_name="qe_relax") - job_qe_minimize.input["calculation"] = "vc-relax" - job_qe_minimize.input.structure = structure - job_qe_minimize.run() - structure_opt = job_qe_minimize.output.structure - - # Energy Volume Curve - energy_lst, volume_lst = [], [] - for i, strain in enumerate(np.linspace(0.9, 1.1, 5)): - structure_strain = structure_opt.copy() - structure_strain = structure.copy() - structure_strain.set_cell( - structure_strain.cell * strain**(1/3), - scale_atoms=True - ) - job_strain = pr.create.job.QEJob( - job_name="job_strain_" + str(i) - ) - job_strain.input.structure = structure_strain - job_strain.run(delete_existing_job=True) - energy_lst.append(job_strain.output.energy) - volume_lst.append(job_strain.output.volume) - - return {"volume": volume_lst, "energy": energy_lst} - - - from pyiron_base import Project - pr = Project("test") - pr.create_job_class( - class_name="QEJob", - write_input_funct=write_input, - collect_output_funct=collect_output, - default_input_dict={ # Default Parameter - "structure": None, - "pseudopotentials": {"Al": "Al.pbe-n-kjpaw_psl.1.0.0.UPF"}, - "kpts": (3, 3, 3), - "calculation": "scf", - }, - executable_str="mpirun -np 1 pw.x -in input.pwi > output.pwo", - ) - - job_workflow = pr.wrap_python_function(workflow) - job_workflow.input.project = pr - job_workflow.input.structure = bulk('Al', a=4.15, cubic=True) - job_workflow.run() - - plt.plot(job_workflow.output.result["volume"], job_workflow.output.result["energy"]) - plt.xlabel("Volume") - plt.ylabel("Energy") +import os +import matplotlib.pyplot as plt +import numpy as np +from ase.build import bulk +from ase.calculators.espresso import Espresso +from ase.io import write +from pwtools import io + + +def write_input(input_dict, working_directory="."): + filename = os.path.join(working_directory, "input.pwi") + os.makedirs(working_directory, exist_ok=True) + write( + filename=filename, + images=input_dict["structure"], + Crystal=True, + kpts=input_dict["kpts"], + input_data={"calculation": input_dict["calculation"]}, + pseudopotentials=input_dict["pseudopotentials"], + tstress=True, + tprnfor=True, + ) + + +def collect_output(working_directory="."): + filename = os.path.join(working_directory, "output.pwo") + try: + return {"structure": io.read_pw_md(filename)[-1].get_ase_atoms()} + except TypeError: + out = io.read_pw_scf(filename) + return { + "energy": out.etot, + "volume": out.volume, + } + + +def workflow(project, structure): + # Structure optimization + job_qe_minimize = pr.create.job.QEJob(job_name="qe_relax") + job_qe_minimize.input["calculation"] = "vc-relax" + job_qe_minimize.input.structure = structure + job_qe_minimize.run() + structure_opt = job_qe_minimize.output.structure + + # Energy Volume Curve + energy_lst, volume_lst = [], [] + for i, strain in enumerate(np.linspace(0.9, 1.1, 5)): + structure_strain = structure_opt.copy() + structure_strain = structure.copy() + structure_strain.set_cell( + structure_strain.cell * strain ** (1 / 3), scale_atoms=True + ) + job_strain = pr.create.job.QEJob(job_name="job_strain_" + str(i)) + job_strain.input.structure = structure_strain + job_strain.run(delete_existing_job=True) + energy_lst.append(job_strain.output.energy) + volume_lst.append(job_strain.output.volume) + + return {"volume": volume_lst, "energy": energy_lst} + + +from pyiron_base import Project + +pr = Project("test") +pr.create_job_class( + class_name="QEJob", + write_input_funct=write_input, + collect_output_funct=collect_output, + default_input_dict={ # Default Parameter + "structure": None, + "pseudopotentials": {"Al": "Al.pbe-n-kjpaw_psl.1.0.0.UPF"}, + "kpts": (3, 3, 3), + "calculation": "scf", + }, + executable_str="mpirun -np 1 pw.x -in input.pwi > output.pwo", +) + +job_workflow = pr.wrap_python_function(workflow) +job_workflow.input.project = pr +job_workflow.input.structure = bulk("Al", a=4.15, cubic=True) +job_workflow.run() + +plt.plot(job_workflow.output.result["volume"], job_workflow.output.result["energy"]) +plt.xlabel("Volume") +plt.ylabel("Energy") ``` After the definition of the `write_input()` and `collect_output()` function for the quantum espresso DFT simulation code diff --git a/docs/developer.md b/docs/developer.md index 0cb080814..5fe83af59 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -118,11 +118,11 @@ and submits this into the queue. I.e. the command running will be ```python command = ( - "python -m pyiron_base.cli wrapper -p " - + job.working_directory - + " -j " - + str(job.job_id) - ) + "python -m pyiron_base.cli wrapper -p " + + job.working_directory + + " -j " + + str(job.job_id) +) ``` which essentially does a `job.load()` and a `job.run()` on the compute node. diff --git a/docs/example.md b/docs/example.md index ca053873e..9bd38bbcd 100644 --- a/docs/example.md +++ b/docs/example.md @@ -9,17 +9,17 @@ from ase.io import write def write_input(input_dict, working_directory="."): - filename = os.path.join(working_directory, 'input.pwi') + filename = os.path.join(working_directory, "input.pwi") os.makedirs(working_directory, exist_ok=True) write( - filename=filename, - images=input_dict["structure"], - Crystal=True, - kpts=input_dict["kpts"], - input_data={"calculation": input_dict["calculation"]}, + filename=filename, + images=input_dict["structure"], + Crystal=True, + kpts=input_dict["kpts"], + input_data={"calculation": input_dict["calculation"]}, pseudopotentials=input_dict["pseudopotentials"], - tstress=True, - tprnfor=True + tstress=True, + tprnfor=True, ) ``` The `write_input()` function takes a dictionary `input_dict` and the path to the working directory `working_directory` @@ -33,7 +33,7 @@ from pwtools import io def collect_output(working_directory="."): - filename = os.path.join(working_directory, 'output.pwo') + filename = os.path.join(working_directory, "output.pwo") try: return {"structure": io.read_pw_md(filename)[-1].get_ase_atoms()} except TypeError: @@ -54,30 +54,27 @@ of five different strains ranging from 90% to 110%. import numpy as np -def workflow(project, structure): - # Structure optimization +def workflow(project, structure): + # Structure optimization job_qe_minimize = project.create.job.QEJob(job_name="qe_relax") job_qe_minimize.input["calculation"] = "vc-relax" job_qe_minimize.input.structure = structure job_qe_minimize.run() structure_opt = job_qe_minimize.output.structure - # Energy Volume Curve + # Energy Volume Curve energy_lst, volume_lst = [], [] for i, strain in enumerate(np.linspace(0.9, 1.1, 5)): structure_strain = structure_opt.copy() structure_strain.set_cell( - structure_strain.cell * strain**(1/3), - scale_atoms=True - ) - job_strain = project.create.job.QEJob( - job_name="job_strain_" + str(i) + structure_strain.cell * strain ** (1 / 3), scale_atoms=True ) + job_strain = project.create.job.QEJob(job_name="job_strain_" + str(i)) job_strain.input.structure = structure_strain job_strain.run(delete_existing_job=True) energy_lst.append(job_strain.output.energy) volume_lst.append(job_strain.output.volume) - + return {"volume": volume_lst, "energy": energy_lst} ``` After the definition of the individual functions it is time to put the different parts together. This part again starts @@ -93,18 +90,18 @@ pr.create_job_class( class_name="QEJob", write_input_funct=write_input, collect_output_funct=collect_output, - default_input_dict={ # Default Parameter - "structure": None, - "pseudopotentials": {"Al": "Al.pbe-n-kjpaw_psl.1.0.0.UPF"}, + default_input_dict={ # Default Parameter + "structure": None, + "pseudopotentials": {"Al": "Al.pbe-n-kjpaw_psl.1.0.0.UPF"}, "kpts": (3, 3, 3), - "calculation": "scf", + "calculation": "scf", }, executable_str="mpirun -np 1 pw.x -in input.pwi > output.pwo", ) job_workflow = pr.wrap_python_function(workflow) job_workflow.input.project = pr -job_workflow.input.structure = bulk('Al', a=4.15, cubic=True) +job_workflow.input.structure = bulk("Al", a=4.15, cubic=True) job_workflow.run() plt.plot(job_workflow.output.result["volume"], job_workflow.output.result["energy"]) diff --git a/docs/installation.md b/docs/installation.md index d4b258aa8..ccdc9ce8d 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -148,6 +148,7 @@ on the [python simple queuing system documentation](https://pysqa.readthedocs.io To test your queuing system configuration use the [python simple queuing system](https://pysqa.readthedocs.io) interface: ```python from pysqa import QueueAdapter + qa = QueueAdapter(directory="/home//resources/queues") print(qa.queue_list) ``` @@ -157,6 +158,7 @@ code above is executed. To validate the `~/.pyiron` configuration file, after the `queue.yaml` file is correctly configured you can use: ```python from pyiron_base import state + print(state.settings._configuration) ``` The `state` object is commonly used to represent the `~/.pyiron` configuration on the python side. diff --git a/docs/tutorial.md b/docs/tutorial.md index 5d99b9304..ed2daedf1 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -98,7 +98,7 @@ distribution over multiple compute nodes and handling the submission to the queu In this example a very simple python function is used: ```python def test_function(a, b=8): - return a+b + return a + b ``` The run time for this function is far below a millisecond so is it not reasonable to submit it to a remote computing cluster. This is primarily a demonstration to highlight the capabilities of the `pyiron_base` workflow manager. @@ -124,7 +124,7 @@ To submit the function call to a remote HPC cluster the server object of the job ```python job.server.queue = "slurm" job.server.cores = 120 -job.server.run_time = 3600 # in seconds +job.server.run_time = 3600 # in seconds ``` In this example the `slurm` queue was selected, a total of 120 CPU cores were selected and a run time of one hour was selected. Still it is important to mention, that assigning 120 CPU cores does not enable parallel execution of the python @@ -142,7 +142,8 @@ components, a `write_input()` function, a `collect_output()` function and finall after the input files were written. The `write_input()` function takes a `input_dict` dictionary and a `working_directory` as input parameters and writes the input files into the working directory. ```python -import os +import os + def write_input(input_dict, working_directory="."): with open(os.path.join(working_directory, "input_file"), "w") as f: @@ -201,10 +202,11 @@ python commands some users prefer to submit a whole Jupyter notebook at once. So introduces the `ScriptJob` job type: ```python from pyiron_base import Project + pr = Project(path="demo") script = pr.create.job.ScriptJob(job_name="script") script.script_path = "demo.ipynb" -script.input['my_variable'] = 5 +script.input["my_variable"] = 5 script.run() ``` The `ScriptJob` jobs take a jupyter notebook as an input `script.script_path` as well as a series of input parameters @@ -227,7 +229,7 @@ The pyiron table object takes three kinds of inputs. The first is a filter funct the following functions are applied on. The filter function is not mandatory still it is very helpful in particular when a large number of jobs are created in a given project. ```python -def myfilter(job): +def myfilter(job): return "test_function" in job.job_name ``` The second part are the selection of functions which are applied on all job objects in a given project. Again it takes diff --git a/specs/job_type.md b/specs/job_type.md index c11b215f2..a2deaec88 100644 --- a/specs/job_type.md +++ b/specs/job_type.md @@ -3,9 +3,10 @@ In pyiron jobs can be very conveniantly be initialized from the `Project` instance: ```python from pyiron import Project -pr = Project('test') -job = pr.create.job.MyJob('job_name') +pr = Project("test") + +job = pr.create.job.MyJob("job_name") ``` ### Job registration @@ -15,7 +16,7 @@ To enable this feature without the burden to `import` all available `JobClasses` ```python from pyiron_base import JobType -JobType.register('pyiron_module.submodule.my_job_class_module', 'MyJobClass') +JobType.register("pyiron_module.submodule.my_job_class_module", "MyJobClass") ``` The register `@classmethod` performs some (minor) sanity checks, in particular raises an error whenever there is already such a `MyJobClass` registered with a different class. To replace an already existing job class, an `overwrite` argument is available. @@ -38,6 +39,6 @@ class MyBaseJobClass(GenericJob): It is under consideration to enhance the job registration by providing a scope for the job a la ```python -JobType.register('module.path', 'JobClass', branch='atomistics', tag='DFT') +JobType.register("module.path", "JobClass", branch="atomistics", tag="DFT") ``` which would allow to filter the available job types by these extra inforation.