Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐦 update evolutionary search #291

Merged
merged 7 commits into from
Sep 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions benchmarks/creators/00_create_and_log_times.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def time_test_creation(creator_class, creator_kwargs):
from simmate.toolkit.creators.structure.third_party.xtalopt import XtaloptStructure

RandomSymStructure.name = "Simmate"
# RandomSymStructure.name = "Simmate (strict)"
XtaloptStructure.name = "XtalOpt"
AseStructure.name = "ASE"
PyXtalStructure.name = "PyXtal"
Expand Down
1 change: 1 addition & 0 deletions benchmarks/creators/01_creation_times_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

CREATORS_TO_TEST = [
"Simmate",
"Simmate (strict)", # 0.75 packing factor for dist cutoffs
"XtalOpt",
"ASE",
"PyXtal",
Expand Down
1 change: 1 addition & 0 deletions benchmarks/creators/02_submit_calcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

CREATORS_TO_TEST = [
# "Simmate", done
"Simmate (strict)",
# "XtalOpt", done
# "ASE", done
# "PyXtal", done
Expand Down
15 changes: 15 additions & 0 deletions benchmarks/creators/04_calc_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
),
yaxis=dict(
title_text="Calculation time (min)",
type="log",
ticks="outside",
tickwidth=2,
showline=True,
Expand All @@ -106,5 +107,19 @@
)

fig = go.Figure(data=subplots, layout=layout)
fig.update_xaxes(
categoryorder="array",
categoryarray=[
"Fe",
"Si",
"C",
"TiO2",
"SiO2",
"Al2O3",
"Si2N2O",
"SrSiN2",
"MgSiO3",
],
)

plot(fig, config={"scrollZoom": True})
1 change: 1 addition & 0 deletions benchmarks/creators/05_fingerprint_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

CREATORS_TO_TEST = [
"Simmate",
"Simmate (strict)",
"XtalOpt",
"ASE",
"PyXtal",
Expand Down
3 changes: 2 additions & 1 deletion docs/change_log.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ There is one key exception to the rules above -- and that is with `MAJOR`=0 rele
- add `show-stats`, `delete-finished`, and `delete-all` commands to `workflow-engine`
- add `Cluster` base class + commands that allow submitting a steady-state cluster via subprocesses or slurm
- add `started_at`, `created_at`, `total_time`, and `queue_time` columns to `Calculation` tables

- add `exlcude_from_archives` field to workflows to optionally delete files when compressing outputs to zip archives
- various improvements added for evolutionary search workflows, such as parameter optimization, new output files, and website views

--------------------------------------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion docs/getting_started/evolutionary_search/quick_start.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ In this tutorial, you will learn how to submit an evolutionary search and other
from different computers. Your calculations will be **slower** and
**error-prone** with sqlite.

If you are seeing the error `Database Connection Closed`, then you have
If you are seeing the error `database is locked`, then you have
exceeded the capabilities of sqlite.

!!! warning
Expand Down
1 change: 1 addition & 0 deletions src/simmate/calculators/vasp/workflows/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class VaspWorkflow(S3Workflow):
_parameter_methods = S3Workflow._parameter_methods + ["_get_clean_structure"]

required_files = ["INCAR", "POTCAR", "POSCAR"]
exlcude_from_archives = ["POTCAR"]

command: str = "vasp_std > vasp.out"
"""
Expand Down
150 changes: 101 additions & 49 deletions src/simmate/calculators/vasp/workflows/relaxation/staged.py
Original file line number Diff line number Diff line change
@@ -1,72 +1,63 @@
# -*- coding: utf-8 -*-

import math
from functools import cache
from pathlib import Path

import numpy
import plotly.graph_objects as plotly_go
from plotly.subplots import make_subplots

from simmate.calculators.vasp.workflows.relaxation.quality00 import (
Relaxation__Vasp__Quality00,
)
from simmate.calculators.vasp.workflows.relaxation.quality01 import (
Relaxation__Vasp__Quality01,
)
from simmate.calculators.vasp.workflows.relaxation.quality02 import (
Relaxation__Vasp__Quality02,
)
from simmate.calculators.vasp.workflows.relaxation.quality03 import (
Relaxation__Vasp__Quality03,
)
from simmate.calculators.vasp.workflows.relaxation.quality04 import (
Relaxation__Vasp__Quality04,
)
from simmate.calculators.vasp.workflows.static_energy.quality04 import (
StaticEnergy__Vasp__Quality04,
)
from simmate.toolkit import Structure
from simmate.visualization.plotting import PlotlyFigure
from simmate.workflow_engine import Workflow

# from simmate.calculators.vasp.database.relaxation import StagedRelaxation
from simmate.workflows.utilities import get_workflow


class Relaxation__Vasp__Staged(Workflow):
"""
Runs a series of increasing-quality relaxations and then finishes with a single
static energy calculation.

This is therefore a "Nested Workflow" made of the following smaller workflows:

- relaxation.vasp.quality00
- relaxation.vasp.quality01
- relaxation.vasp.quality02
- relaxation.vasp.quality03
- relaxation.vasp.quality04
- static-energy.vasp.quality04

This workflow is most useful for randomly-created structures or extremely
large supercells. More precise relaxations+energy calcs should be done
afterwards because ettings are still below MIT and Materials Project quality.
"""

exlcude_from_archives = [
"CHG",
"CHGCAR",
"DOSCAR",
"EIGENVAL",
"IBZKPT",
"OSZICAR",
"OUTCAR",
"PCDAT",
"POTCAR",
"REPORT",
"WAVECAR",
"XDATCAR",
]

description_doc_short = "runs a series of relaxations (00-04 quality)"

subworkflows = [
Relaxation__Vasp__Quality00,
Relaxation__Vasp__Quality01,
Relaxation__Vasp__Quality02,
Relaxation__Vasp__Quality03,
Relaxation__Vasp__Quality04,
StaticEnergy__Vasp__Quality04,
subworkflow_names = [
"relaxation.vasp.quality00",
"relaxation.vasp.quality01",
"relaxation.vasp.quality02",
"relaxation.vasp.quality03",
"relaxation.vasp.quality04",
"static-energy.vasp.quality04",
]

@classmethod
def run_config(
cls,
structure,
command=None,
source=None,
directory=None,
copy_previous_directory=False,
structure: Structure,
command: str = None,
source: dict = None,
directory: Path = None,
copy_previous_directory: bool = False,
**kwargs,
):

Expand All @@ -81,7 +72,6 @@ def run_config(

# The remaining tasks continue and use the past results as an input
for i, current_task in enumerate(cls.subworkflows[1:]):
preceding_task = cls.subworkflows[i] # will be one before because of [:1]
state = current_task.run(
structure=result, # this is the result of the last run
command=command,
Expand All @@ -103,13 +93,20 @@ def run_config(
return final_result

@classmethod
def get_energy_series(cls, **filter_kwargs):
@property
@cache
def subworkflows(cls):
return [get_workflow(name) for name in cls.subworkflow_names]

@classmethod
def get_series(cls, value: str, **filter_kwargs):
directories = (
cls.all_results.filter(**filter_kwargs)
.values_list("directory", flat=True)
.all()
)

# OPTIMIZE: This is a query for EACH entry which is very inefficient
all_energy_series = []
for directory in directories:
energy_series = []
Expand All @@ -118,12 +115,12 @@ def get_energy_series(cls, **filter_kwargs):
workflow_name=subflow.name_full,
directory__startswith=directory,
energy_per_atom__isnull=False,
).only("energy_per_atom")
).values_list(value)
if query.exists():
result = query.get()
energy_series.append(result.energy_per_atom)
energy_series.append(result[0])
else:
energy_series.append(None)
energy_series.append(numpy.nan)
all_energy_series.append(energy_series)

return all_energy_series
Expand All @@ -136,7 +133,10 @@ def get_plot(
workflow, # Relaxation__Vasp__Staged
**filter_kwargs,
):
all_energy_series = workflow.get_energy_series(**filter_kwargs)
all_energy_series = workflow.get_series(
value="energy_per_atom",
**filter_kwargs,
)

figure = make_subplots(
rows=math.ceil((len(workflow.subworkflows) - 1) / 3),
Expand Down Expand Up @@ -192,7 +192,10 @@ def get_plot(
**filter_kwargs,
):

all_energy_series = workflow.get_energy_series(**filter_kwargs)
all_energy_series = workflow.get_series(
value="energy_per_atom",
**filter_kwargs,
)

figure = plotly_go.Figure()

Expand Down Expand Up @@ -229,6 +232,55 @@ def get_plot(
return figure


class StagedSeriesTimes(PlotlyFigure):

method_type = "classmethod"

def get_plot(
workflow, # Relaxation__Vasp__Staged
**filter_kwargs,
):

all_time_series = workflow.get_series(
value="total_time",
**filter_kwargs,
)

figure = plotly_go.Figure()

all_time_series = numpy.transpose(all_time_series)
all_time_series = all_time_series / 60 # convert to minutes
traces = []
for i, times in enumerate(all_time_series):

trace = plotly_go.Histogram(
x=times,
name=f"{workflow.subworkflows[i].name_full}",
)
traces.append(trace)

# add them to the figure in reverse, so that the first relaxations are
# in the front and not hidden
traces.reverse()
for trace in traces:
figure.add_trace(trace=trace)

figure.update_layout(
barmode="overlay",
xaxis_title_text="Calculation time (min)",
yaxis_title_text="Structures (#)",
bargap=0.05,
legend=dict(
yanchor="top",
y=0.99,
xanchor="left",
x=0.01,
),
)
figure.update_traces(opacity=0.75)
return figure


# register all plotting methods to the database table
for _plot in [StagedSeriesConvergence, StagedSeriesHistogram]:
for _plot in [StagedSeriesConvergence, StagedSeriesHistogram, StagedSeriesTimes]:
_plot.register_to_class(Relaxation__Vasp__Staged)
Loading