Skip to content

Commit

Permalink
Added SimulationWorkflow, SinglePoint
Browse files Browse the repository at this point in the history
Added DFTPlusTB and method sections for workflow
  • Loading branch information
JosePizarro3 committed Sep 17, 2024
1 parent bd2f695 commit 0f0f81c
Show file tree
Hide file tree
Showing 4 changed files with 452 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/nomad_simulations/schema_packages/workflow/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#
# Copyright The NOMAD Authors.
#
# This file is part of NOMAD.
# See https://nomad-lab.eu for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from .base_workflows import BeyondDFTMethod, BeyondDFTWorkflow, SimulationWorkflow
from .dft_plus_tb import DFTPlusTB, DFTPlusTBMethod
from .single_point import SinglePoint
143 changes: 143 additions & 0 deletions src/nomad_simulations/schema_packages/workflow/base_workflows.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#
# Copyright The NOMAD Authors.
#
# This file is part of NOMAD.
# See https://nomad-lab.eu for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from typing import TYPE_CHECKING, Optional

if TYPE_CHECKING:
from nomad.datamodel.datamodel import EntryArchive
from structlog.stdlib import BoundLogger

from nomad.datamodel.data import ArchiveSection
from nomad.datamodel.metainfo.workflow import Link, Task, Workflow
from nomad.metainfo import SubSection

from nomad_simulations.schema_packages.model_method import BaseModelMethod
from nomad_simulations.schema_packages.model_system import ModelSystem
from nomad_simulations.schema_packages.outputs import Outputs


class SimulationWorkflow(Workflow):
"""
A base section used to define the workflows of a simulation with references to specific `tasks`, `inputs`, and `outputs`. The
normalize function checks the definition of these sections and sets the name of the workflow.
A `SimulationWorkflow` will be composed of:
- a `method` section containing methodological parameters used specifically during the workflow,
- a list of `inputs` with references to the `ModelSystem` or `ModelMethod` input sections,
- a list of `outputs` with references to the `Outputs` section,
- a list of `tasks` containing references to the activity `Simulation` used in the workflow,
"""

method = SubSection(
sub_section=BaseModelMethod.m_def,
description="""Methodological parameters used during the workflow.""",
)

def resolve_inputs_outputs(
self, archive: 'EntryArchive', logger: 'BoundLogger'
) -> None:
"""
Resolves the `inputs` and `outputs` sections from the archive sections under `data` and stores
them in private attributes.
Args:
archive (EntryArchive): The archive to resolve the sections from.
logger (BoundLogger): The logger to log messages.
"""
if (
not archive.data.model_system
or not archive.data.model_method
or not archive.data.outputs
):
logger.info(
'`ModelSystem`, `ModelMethod` and `Outputs` required for normalization of `SimulationWorkflow`.'
)
return None
self._input_systems = archive.data.model_system
self._input_methods = archive.data.model_method
self._outputs = archive.data.outputs

# Resolve `inputs`
if not self.inputs:
self.m_add_sub_section(
Workflow.inputs,
Link(name='Input Model System', section=self._input_systems[0]),
)
# Resolve `outputs`
if not self.outputs:
self.m_add_sub_section(
Workflow.outputs,
Link(name='Output Data', section=self._outputs[-1]),
)

def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None:
super().normalize(archive, logger)

# Resolve the `inputs` and `outputs` from the archive
self.resolve_inputs_outputs(archive=archive, logger=logger)

# Storing the initial `ModelSystem`
for link in self.inputs:
if isinstance(link.section, ModelSystem):
self.initial_structure = link.section
break


class BeyondDFTMethod(ArchiveSection):
"""
An abstract section used to store references to the `ModelMethod` sections of each of the
archives defining the `tasks` and used to build the standard workflow. This section needs to be
inherit and the method references need to be defined for each specific case.
"""

def resolve_beyonddft_method_ref(self, task: Task) -> Optional[BaseModelMethod]:
"""
Resolves the `ModelMethod` reference for the `task`.
Args:
task (Task): The task to resolve the `ModelMethod` reference from.
Returns:
Optional[BaseModelMethod]: The resolved `ModelMethod` reference.
"""
for input in task.inputs:
if input.name == 'Input Model Method':
return input.section
return None


class BeyondDFTWorkflow(SimulationWorkflow):
method = SubSection(sub_section=BeyondDFTMethod.m_def)

def resolve_all_outputs(self) -> list[Outputs]:
"""
Resolves all the `Outputs` sections from the `tasks` in the workflow. This is useful when
the workflow is composed of multiple tasks and the outputs need to be stored in a list
for further manipulation, e.g., to plot multiple band structures in a DFT+TB workflow.
Returns:
list[Outputs]: A list of all the `Outputs` sections from the `tasks`.
"""
all_outputs = []
for task in self.tasks:
all_outputs.append(task.outputs[-1])
return all_outputs

def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None:
super().normalize(archive, logger)
166 changes: 166 additions & 0 deletions src/nomad_simulations/schema_packages/workflow/dft_plus_tb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#
# Copyright The NOMAD Authors.
#
# This file is part of NOMAD.
# See https://nomad-lab.eu for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from nomad.datamodel.datamodel import EntryArchive
from structlog.stdlib import BoundLogger

from nomad.datamodel.metainfo.workflow import Link
from nomad.metainfo import Quantity, Reference

from nomad_simulations.schema_packages.model_method import BaseModelMethod
from nomad_simulations.schema_packages.properties import FermiLevel
from nomad_simulations.schema_packages.workflow import (
BeyondDFTMethod,
BeyondDFTWorkflow,
)


class DFTPlusTBMethod(BeyondDFTMethod):
"""
Section used to reference the `DFT` and `TB` `ModelMethod` sections in each of the archives
conforming a DFT+TB simulation workflow.
"""

dft_method_ref = Quantity(
type=Reference(BaseModelMethod),
description="""Reference to the DFT `ModelMethod` section in the DFT task.""",
)
tb_method_ref = Quantity(
type=Reference(BaseModelMethod),
description="""Reference to the GW `ModelMethod` section in the TB task.""",
)


class DFTPlusTB(BeyondDFTWorkflow):
"""
DFT+TB workflow is composed of two tasks: the initial DFT calculation + the final TB projection. This
workflow section is used to define the same energy reference for both the DFT and TB calculations, by
setting it up to the DFT calculation. The structure of the workflow is:
- `self.inputs[0]`: the initial `ModelSystem` section in the DFT entry,
- `self.outputs[0]`: the outputs section in the TB entry,
- `tasks[0]`:
- `tasks[0].task` (TaskReference): the reference to the `SinglePoint` task in the DFT entry,
- `tasks[0].inputs[0]`: the initial `ModelSystem` section in the DFT entry,
- `tasks[0].outputs[0]`: the outputs section in the DFT entry,
- `tasks[1]`:
- `tasks[1].task` (TaskReference): the reference to the `SinglePoint` task in the TB entry,
- `tasks[1].inputs[0]`: the outputs section in the DFT entry,
- `tasks[1].outputs[0]`: the outputs section in the TB entry,
- `method`: references to the `ModelMethod` sections in the DFT and TB entries.
"""

def resolve_method(self) -> DFTPlusTBMethod:
"""
Resolves the `DFT` and `TB` `ModelMethod` references for the `tasks` in the workflow by using the
`resolve_beyonddft_method_ref` method from the `BeyondDFTMethod` section.
Returns:
DFTPlusTBMethod: The resolved `DFTPlusTBMethod` section.
"""
method = DFTPlusTBMethod()

# DFT method reference
dft_method = method.resolve_beyonddft_method_ref(task=self.tasks[0].task)
if dft_method is not None:
method.dft_method_ref = dft_method

# TB method reference
tb_method = method.resolve_beyonddft_method_ref(task=self.tasks[1].task)
if tb_method is not None:
method.tb_method_ref = tb_method

return method

def link_tasks(self) -> None:
"""
Links the `outputs` of the DFT task with the `inputs` of the TB task.
"""
dft_task = self.tasks[0]
dft_task.inputs = [
Link(
name='Input Model System',
section=self.inputs[0],
)
]
dft_task.outputs = [
Link(
name='Output DFT Data',
section=dft_task.outputs[-1],
)
]

tb_task = self.tasks[1]
tb_task.inputs = [
Link(
name='Output DFT Data',
section=dft_task.outputs[-1],
),
]
tb_task.outputs = [
Link(
name='Output TB Data',
section=tb_task.outputs[-1],
)
]

def overwrite_fermi_level(self) -> None:
"""
Overwrites the Fermi level in the TB calculation with the Fermi level from the DFT calculation.
"""
dft_output = self.tasks[0].outputs[-1]
if not dft_output.fermi_levels:
return None
fermi_level = dft_output.fermi_levels[-1]

tb_output = self.tasks[1].outputs[-1]
tb_output.fermi_levels.append(FermiLevel(value=fermi_level.value))

def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None:
super().normalize(archive, logger)

# Initial check for the number of tasks
if len(self.tasks) != 2:
logger.error('A `DFTPlusTB` workflow must have two tasks.')
return

# Check if tasks are `SinglePoint`
for task in self.tasks:
if task.m_def.name != 'SinglePoint':
logger.error(
'A `DFTPlusTB` workflow must have two `SinglePoint` tasks.'
)
return

# Define names of the workflow and `tasks`
self.name = 'DFT+TB'
self.tasks[0].name = 'DFT SinglePoint'
self.tasks[1].name = 'TB SinglePoint'

# Resolve method refs for each task and store under `method`
self.method = self.resolve_method()

# Link the tasks
self.link_tasks()

# Overwrite the Fermi level in the TB calculation
self.overwrite_fermi_level()
Loading

1 comment on commit 0f0f81c

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/nomad_simulations
   __init__.py4250%3–4
   _version.py11282%5–6
src/nomad_simulations/schema_packages
   __init__.py14286%53–55
   atoms_state.py1902189%31–33, 219–222, 246, 301–302, 370–371, 373, 555, 567–568, 629–633, 648–652, 659
   general.py75791%29–30, 98, 162, 272–273, 283
   model_method.py2657771%28–30, 189–192, 195–202, 294–295, 315, 336–355, 371–397, 400–417, 771, 782, 824–831, 869, 888, 968, 1025, 1100, 1214
   model_system.py2612292%43–45, 512–515, 562–569, 743–744, 965–969, 975–976, 984–985, 990, 1013
   numerical_settings.py2636575%30–32, 164, 234, 236–237, 240–243, 247–248, 255–258, 267–270, 274–277, 279–282, 287–290, 296–299, 470–497, 572, 607–610, 634, 637, 682, 684–687, 691, 695, 742, 746–767, 822–823, 890, 899–901, 904
   outputs.py1151091%27–28, 247–250, 290–293, 318, 320, 357, 376
   physical_property.py102793%38–40, 220, 349–351
   variables.py651183%26–28, 116, 139, 159–160, 163, 185, 208, 228
src/nomad_simulations/schema_packages/properties
   band_gap.py51590%26–28, 153–154
   band_structure.py1112280%27–29, 249–282, 295, 302, 338–339, 342
   energies.py36975%25–27, 54, 75, 100, 121, 131, 140
   fermi_surface.py17476%25–27, 58
   forces.py22673%26–28, 55, 75, 98
   hopping_matrix.py29583%25–27, 75, 110
   permittivity.py48883%25–27, 115–123
   spectral_profile.py25612452%27–29, 75–78, 113–116, 217–318, 374–386, 411–414, 434, 439–442, 484–520, 544, 591–594, 610–611, 616–622
   thermodynamics.py751876%25–27, 53, 74, 90, 99, 108, 119, 128, 155, 165, 175, 195, 211, 236, 252, 277
src/nomad_simulations/schema_packages/utils
   utils.py681479%26–29, 79–88, 97–98, 103, 106
src/nomad_simulations/schema_packages/workflow
   __init__.py330%20–22
   base_workflows.py45450%20–143
   dft_plus_tb.py51510%20–166
   single_point.py36360%40–121
TOTAL222357674% 

Tests Skipped Failures Errors Time
321 0 💤 28 ❌ 0 🔥 6.407s ⏱️

Please sign in to comment.