Skip to content

Commit

Permalink
Merge pull request #134 from pyiron/fixr
Browse files Browse the repository at this point in the history
Fix duplicate and inconsistant base/atomistics

Also black assyst module
  • Loading branch information
pmrv authored Aug 15, 2024
2 parents c7efa1e + 25e98bb commit 31b3939
Show file tree
Hide file tree
Showing 18 changed files with 199 additions and 117 deletions.
6 changes: 3 additions & 3 deletions .ci_support/exclude
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
spgfit/01_Structures.ipynb
spgfit/02_Calculations.ipynb
spgfit/03_Learn.ipynb
assyst/01_Structures.ipynb
assyst/02_Calculations.ipynb
assyst/03_Learn.ipynb
MTP_Example.ipynb
1 change: 1 addition & 0 deletions .github/workflows/push-pull.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ jobs:
secrets: inherit
with:
notebooks-env-files: .ci_support/environment.yml .ci_support/environment-notebooks.yml
python-version-alt3: exclude
6 changes: 2 additions & 4 deletions pyiron_potentialfit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,10 @@

# for module in moved_potential_modules:
add_module_conversion(
"pyiron_potentialfit.spgfit.structures",
"pyiron_potentialfit.assyst.structures"
"pyiron_potentialfit.spgfit.structures", "pyiron_potentialfit.assyst.structures"
)
add_module_conversion(
"pyiron_potentialfit.spgfit.projectflow",
"pyiron_potentialfit.assyst.projectflow"
"pyiron_potentialfit.spgfit.projectflow", "pyiron_potentialfit.assyst.projectflow"
)

from ._version import get_versions
Expand Down
10 changes: 5 additions & 5 deletions pyiron_potentialfit/assyst/calculations/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from .workflow import (
CalculationConfig,
ServerConfig,
VaspConfig,
WorkflowProjectConfig,
run
CalculationConfig,
ServerConfig,
VaspConfig,
WorkflowProjectConfig,
run,
)
from ..projectflow import RunAgain
31 changes: 24 additions & 7 deletions pyiron_potentialfit/assyst/calculations/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def collect(
train.run()
return train


@dataclass
class CalculationConfig:
vasp: VaspConfig
Expand Down Expand Up @@ -127,13 +128,17 @@ def if_new(train):
if train.input.read_only:
train.input.unlock()
if config.vasp.magmoms is not None and len(config.vasp.magmoms) > 0:

def apply_magmom(structure):
if not structure.has('initial_magmoms'):
if not structure.has("initial_magmoms"):
structure.set_initial_magnetic_moments(
[config.vasp.magmoms.get(sym, 0.0) for sym in structure.symbols]
[config.vasp.magmoms.get(sym, 0.0) for sym in structure.symbols]
)
return structure
filtered_cont = filtered_cont.transform_structures(apply_magmom).collect_structures()

filtered_cont = filtered_cont.transform_structures(
apply_magmom
).collect_structures()
train.input.structures = filtered_cont

train.input.job = config.get_job()
Expand All @@ -149,15 +154,25 @@ def if_finished(train):
return results

with warnings.catch_warnings():
warnings.filterwarnings("ignore", message="'KSPACING' found in INCAR, no KPOINTS file written",)
warnings.filterwarnings(
"ignore",
message="'KSPACING' found in INCAR, no KPOINTS file written",
)
return train.check(
config.workflow,
if_new,
if_finished,
number_of_jobs=train.input.structures.number_of_structures,
)

def run(pr: Project, config: CalculationConfig, *containers: "StructureContainer", tries: int = 10, wait: float = 60):

def run(
pr: Project,
config: CalculationConfig,
*containers: "StructureContainer",
tries: int = 10,
wait: float = 60
):
"""
Run high quality DFT on all structures in `containers`.
Expand Down Expand Up @@ -193,7 +208,9 @@ def run(pr: Project, config: CalculationConfig, *containers: "StructureContainer
if i + 1 < tries:
time.sleep(wait)
if retry:
warnings.warn("Structure creation is not finished! Call this function again later!")
warnings.warn(
"Structure creation is not finished! Call this function again later!"
)


def deduplicate(cont, replace=True):
Expand Down Expand Up @@ -266,7 +283,7 @@ def combine(
df = cont.to_pandas()
df["name"] = df.name.map(lambda s: cont.name + "_" + s)
if energy_cap is not None:
I = df.energy/df.number_of_atoms <= energy_cap
I = df.energy / df.number_of_atoms <= energy_cap
df = df.loc[I]
if force_cap is not None:
I = df.forces.map(lambda f: np.linalg.norm(f, axis=-1).max() < force_cap)
Expand Down
10 changes: 5 additions & 5 deletions pyiron_potentialfit/assyst/structures/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .workflow import (
TrainingDataConfig,
ServerConfig,
VaspConfig,
WorkflowProjectConfig,
run,
TrainingDataConfig,
ServerConfig,
VaspConfig,
WorkflowProjectConfig,
run,
)
11 changes: 6 additions & 5 deletions pyiron_potentialfit/assyst/structures/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@

from .workflow import export_structures, run, TrainingDataConfig


class AssystStructures(PythonTemplateJob):
"""
Create structure set with ASSYST.
"""

def __init__(self, project, job_name):
super().__init__(project, job_name)
self.input.update(asdict(TrainingDataConfig(
elements=["Mg"],
name=None
)))
self.input.update(asdict(TrainingDataConfig(elements=["Mg"], name=None)))

@property
def child_project(self):
Expand All @@ -26,7 +25,9 @@ def run_static(self):
self.status.running = True
run(self.child_project, TrainingDataConfig(**self.input), tries=None)
self.status.collect = True
for cont in self.child_project["containers"].iter_jobs(hamilton="StructureContainer"):
for cont in self.child_project["containers"].iter_jobs(
hamilton="StructureContainer"
):
# proper but loses identifiers
# self.output[cont.name] = cont.collect_structures()
self.output[cont.name] = cont._container.copy()
Expand Down
55 changes: 40 additions & 15 deletions pyiron_potentialfit/assyst/structures/minimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@
)

from pyiron_potentialfit.atomistics.job.trainingcontainer import (
TrainingContainer,
TrainingStorage,
TrainingContainer,
TrainingStorage,
)
from pyiron_atomistics.atomistics.structure.structurestorage import StructureStorage
from pyiron_atomistics.atomistics.job.structurecontainer import StructureContainer
from pyiron_contrib.jobfactories import VaspFactory

from traitlets import Instance, Float, Bool, Int, CaselessStrEnum, Dict


class MinimizeVaspInput(Input):

symlink = Bool(default_value=False, help="Whether to symlink the project or not")
Expand All @@ -45,10 +46,12 @@ class MinimizeVaspInput(Input):
min_dist = Float(default_value=None, allow_none=True)
accept_not_converged = Int(default_value=False)


class MinimizeOutput(Output):
final_structures = Instance(TrainingStorage, args=())
trace_structures = Instance(TrainingStorage, args=())


class MinimizeVaspFlow(ProjectFlow):

Input = MinimizeVaspInput
Expand Down Expand Up @@ -80,45 +83,61 @@ def _run(self, delete_existing_job=False, delete_aborted_job=True):

sflow.input.job = vasp
if vasp_config.magmoms is not None and len(vasp_config.magmoms) > 0:

def apply_magmom(structure):
if not structure.has('initial_magmoms'):
if not structure.has("initial_magmoms"):
structure.set_initial_magnetic_moments(
[vasp_config.magmoms.get(sym, 0.0) for sym in structure.symbols]
[vasp_config.magmoms.get(sym, 0.0) for sym in structure.symbols]
)
return structure
sflow.input.structures = self.input.structures.transform_structures(apply_magmom).collect_structures()

sflow.input.structures = self.input.structures.transform_structures(
apply_magmom
).collect_structures()
else:
sflow.input.structures = self.input.structures.copy()
sflow.input.table_setup = lambda tab: tab

sflow.attach(self.project, "structures").run()

def _analyze(self, delete_existing_job=False):
if self.output.final_structures.number_of_structures > 0 and not delete_existing_job:
if (
self.output.final_structures.number_of_structures > 0
and not delete_existing_job
):
return
ok_status = ["finished"]
if self.input.accept_not_converged:
ok_status += ["not_converged", "warning"]
for j in self.project.iter_jobs(hamilton="Vasp", convert_to_object=False):
if j.status not in ok_status: continue
if j.status not in ok_status:
continue

N = len(j["output/generic/steps"])
stride = max(N // self.input.number_of_structures, 1)
for i in range(1, N + 1, stride)[:self.input.number_of_structures]:
for i in range(1, N + 1, stride)[: self.input.number_of_structures]:
s = self._extract_structure(j, -i)
if (
self.input.min_dist is not None
and np.prod(s.get_neighbors(cutoff_radius=self.input.min_dist).distances.shape)
and np.prod(
s.get_neighbors(
cutoff_radius=self.input.min_dist
).distances.shape
)
> 0
):
continue
if self.input.number_of_structures == 1:
name = j.name
else:
name = f"{j.name}_step_{i}"
self.output.trace_structures.include_job(j, iteration_step=-i, identifier=name)
self.output.trace_structures.include_job(
j, iteration_step=-i, identifier=name
)
if i == 1:
self.output.final_structures.include_job(j, iteration_step=-i, identifier=name)
self.output.final_structures.include_job(
j, iteration_step=-i, identifier=name
)

@staticmethod
def _extract_structure(jobpath, frame):
Expand All @@ -144,6 +163,7 @@ def _extract_structure(jobpath, frame):
)
return structure


def minimize(
pr,
cont: StructureContainer,
Expand Down Expand Up @@ -185,7 +205,9 @@ def if_finished(flow):
if cont is None:
cont = flow.project.create.job.StructureContainer("Trace")
for i, s in enumerate(flow.output.trace_structures.iter_structures()):
cont.add_structure(s, identifier=flow.output.trace_structures["identifier", i])
cont.add_structure(
s, identifier=flow.output.trace_structures["identifier", i]
)
cont.run()
cont.copy_to(
pr.create_group("containers"),
Expand All @@ -195,10 +217,13 @@ def if_finished(flow):
if cont is None:
cont = flow.project.create.job.StructureContainer("Final")
for i, s in enumerate(flow.output.final_structures.iter_structures()):
cont.add_structure(s, identifier=flow.output.final_structures["identifier", i])
cont.add_structure(
s, identifier=flow.output.final_structures["identifier", i]
)
cont.run()
cont.copy_to(pr["containers"], new_job_name=flow.project.name)
return cont

return minf.check(workflow, if_new, if_finished,
number_of_jobs=cont.number_of_structures)
return minf.check(
workflow, if_new, if_finished, number_of_jobs=cont.number_of_structures
)
8 changes: 3 additions & 5 deletions pyiron_potentialfit/assyst/structures/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import numpy as np
from tqdm.auto import tqdm


def shake(displacement=0.1):
"""
Return a function that randomly displaces atoms in structures.
Expand Down Expand Up @@ -74,7 +75,7 @@ def fill_container(
# one weird aspect ratios, the neighbor searching code can allocate huge structures,
# because it explicitly repeats the structure to create ghost atoms
# since we only care about the presence of short distances between atoms and not the
# real neighbor information, simply double the structure to make sure we see all bonds
# real neighbor information, simply double the structure to make sure we see all bonds
# and turn off PBC
sd = s.repeat(2)
sd.pbc = [False, False, False]
Expand Down Expand Up @@ -117,10 +118,7 @@ def rattle(
rand,
repetitions=rattle_repetitions,
combine=2,
modifiers=(
(1, shake(rattle_disp)),
(1, stretch(rattle_strain))
),
modifiers=((1, shake(rattle_disp)), (1, stretch(rattle_strain))),
min_dist=min_dist,
)
logger.info("added %i rattle structures", rand.number_of_structures - N)
Expand Down
3 changes: 2 additions & 1 deletion pyiron_potentialfit/assyst/structures/spg.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from ..util import RCORE, DistanceFilter


def _pyxtal(
group: Union[int, List[int]],
species: Tuple[str],
Expand Down Expand Up @@ -179,7 +180,7 @@ def check_cell_shape(structure):
if min_dist is None:
distance_filter = DistanceFilter()
else:
distance_filter = DistanceFilter({e: min_dist/2 for e in elements})
distance_filter = DistanceFilter({e: min_dist / 2 for e in elements})
el, ni = zip(*((el, ni) for el, ni in zip(elements, num_ions) if ni > 0))
# missing checker support
# pr.create.structure.pyxtal(
Expand Down
Loading

0 comments on commit 31b3939

Please sign in to comment.