Stand-alone Usage¶
Purpose¶
Understand how to instantiate schema classes and populate quantities directly in Python, without parser-plugin complexity. The page ends with a short normalization-methods intro.
Assignment 2.1: Simulation timing basics¶
Assignment 2.1
Create a Simulation instance, assign cpu1_start=0 s and cpu1_end=24 min 30 s,
and compute elapsed time in seconds and hours.
Related schema diagrams¶
Solution 2.1
from nomad.units import ureg
from nomad_simulations.schema_packages.general import Simulation
simulation = Simulation()
simulation.cpu1_start = 0 * ureg.second
simulation.cpu1_end = 30 * ureg.second + 24 * ureg.minute
elapsed_seconds = (simulation.cpu1_end - simulation.cpu1_start).to('second').magnitude
elapsed_hours = (simulation.cpu1_end - simulation.cpu1_start).to('hour').magnitude
assert elapsed_seconds == 1470
assert round(elapsed_hours, 6) == round(1470 / 3600, 6)
Assignment 2.2: Program typing and composition¶
Assignment 2.2
Create a Program(name='VASP', version='5.0.0'), attach it to Simulation.program,
then assign an integer to version and inspect the stored result.
Related schema diagrams¶
Solution 2.2
from nomad_simulations.schema_packages.general import Program, Simulation
simulation = Simulation()
program = Program(name='VASP', version='5.0.0')
simulation.program = program
assert simulation.program.name == 'VASP'
assert simulation.program.version == '5.0.0'
# Compatible scalar inputs are auto-converted to string when possible.
program.version = 5
assert program.version == '5'
Assignment 2.3: DFT under Simulation.model_method¶
Assignment 2.3
Instantiate a DFT method and append it to Simulation.model_method.
Use DFT.xc (XCFunctional) to represent XC identity in the current schema.
Related schema diagrams¶
Solution 2.3
from nomad_simulations.schema_packages.general import Simulation
from nomad_simulations.schema_packages.model_method import DFT, XCFunctional
simulation = Simulation()
dft = DFT(name='DFT', type='KS', xc=XCFunctional(functional_key='PBE'))
simulation.model_method.append(dft)
assert isinstance(simulation.model_method[0], DFT)
assert simulation.model_method[0].xc.functional_key == 'PBE'
Assignment 2.4: SelfConsistency in numerical_settings¶
Assignment 2.4
Add SelfConsistency(threshold_change=1e-3, threshold_change_unit='joule')
under DFT.numerical_settings and verify parent-child linkage.
Related schema diagrams¶
Solution 2.4
from nomad_simulations.schema_packages.model_method import DFT
from nomad_simulations.schema_packages.numerical_settings import SelfConsistency
scf = SelfConsistency(threshold_change=1e-3, threshold_change_unit='joule')
dft = DFT(name='DFT', type='KS', numerical_settings=[scf])
assert dft.numerical_settings[0].threshold_change == 1e-3
assert dft.numerical_settings[0].threshold_change_unit == 'joule'
assert scf.m_parent is dft
Assignment 2.5: AtomsState and atomic numbers¶
Assignment 2.5
Create AtomsState entries for Ga and As and resolve atomic numbers.
Related schema diagrams¶
Solution 2.5
from nomad import utils
from nomad_simulations.schema_packages.atoms_state import AtomsState
logger = utils.get_logger(__name__)
atoms_states = [AtomsState(chemical_symbol=symbol) for symbol in ['Ga', 'As']]
atomic_numbers = [atom.resolve_atomic_number(logger=logger) for atom in atoms_states]
assert atomic_numbers == [31, 33]
Assignment 2.6: ModelSystem, particle_states, and chemical formulas¶
Assignment 2.6
Build a representative ModelSystem with positions, lattice vectors,
and particle_states, then normalize and inspect formula variants.
Related schema diagrams¶
Solution 2.6
import numpy as np
from nomad import utils
from nomad.datamodel import EntryArchive
from nomad.units import ureg
from nomad_simulations.schema_packages.atoms_state import AtomsState
from nomad_simulations.schema_packages.model_system import ModelSystem
logger = utils.get_logger(__name__)
model_system = ModelSystem(is_representative=True)
model_system.lattice_vectors = np.eye(3) * 5.65 * ureg.angstrom
model_system.periodic_boundary_conditions = [True, True, True]
model_system.positions = np.array([[0, 0, 0], [2.825, 2.825, 2.825]]) * ureg.angstrom
model_system.particle_states.append(AtomsState(chemical_symbol='Ga'))
model_system.particle_states.append(AtomsState(chemical_symbol='As'))
model_system.normalize(archive=EntryArchive(), logger=logger)
assert model_system.chemical_formula is not None
assert model_system.chemical_formula.iupac == 'GaAs'
assert model_system.chemical_formula.hill == 'AsGa'
assert model_system.chemical_formula.anonymous == 'AB'
Current schema terminology
Legacy AtomicCell/cell/atoms_state patterns are replaced by direct ModelSystem
geometry (positions, lattice_vectors, periodic_boundary_conditions) and
per-particle data in particle_states.
Assignment 2.7: Outputs, references, and SCF deltas¶
Assignment 2.7
Create an Outputs section with SCFSteps and TotalEnergy values across steps,
normalize, and verify automatic model_system_ref, model_method_ref, and
delta_energies_total population.
Related schema diagrams¶
Solution 2.7
import numpy as np
from nomad import utils
from nomad.datamodel import EntryArchive
from nomad.units import ureg
from nomad_simulations.schema_packages.atoms_state import AtomsState
from nomad_simulations.schema_packages.general import Simulation
from nomad_simulations.schema_packages.model_method import DFT
from nomad_simulations.schema_packages.model_system import ModelSystem
from nomad_simulations.schema_packages.numerical_settings import SelfConsistency
from nomad_simulations.schema_packages.outputs import Outputs, SCFSteps
from nomad_simulations.schema_packages.properties import TotalEnergy
logger = utils.get_logger(__name__)
simulation = Simulation()
model_system = ModelSystem(is_representative=True)
model_system.lattice_vectors = np.eye(3) * 5.0 * ureg.angstrom
model_system.periodic_boundary_conditions = [True, True, True]
model_system.positions = np.array([[0, 0, 0], [1.25, 1.25, 1.25]]) * ureg.angstrom
model_system.particle_states.append(AtomsState(chemical_symbol='Si'))
model_system.particle_states.append(AtomsState(chemical_symbol='Si'))
simulation.model_system.append(model_system)
method = DFT(
name='DFT',
type='KS',
numerical_settings=[
SelfConsistency(threshold_change=1e-6, threshold_change_unit='joule')
],
)
simulation.model_method.append(method)
outputs = Outputs(scf_steps=SCFSteps())
for value in [1.0, 1.5, 2.0, 2.1, 2.101]:
outputs.total_energies.append(TotalEnergy(value=value * ureg.eV))
simulation.outputs.append(outputs)
outputs.normalize(archive=EntryArchive(), logger=logger)
assert outputs.model_system_ref is simulation.model_system[0]
assert outputs.model_method_ref is simulation.model_method[0]
assert outputs.scf_steps is not None
assert outputs.scf_steps.delta_energies_total is not None
assert len(outputs.scf_steps.delta_energies_total) == 4
first_delta_joule = outputs.scf_steps.delta_energies_total[0].to('joule').magnitude
assert np.isclose(first_delta_joule, (0.5 * ureg.eV).to('joule').magnitude)
Assignment 2.8: output-property collections and spin channels¶
Assignment 2.8
Store scalar and spin-resolved ElectronicBandGap entries in Outputs and
extract spin-polarized entries with extract_spin_polarized_property.
Related schema diagrams¶
Solution 2.8
from nomad.units import ureg
from nomad_simulations.schema_packages.outputs import Outputs
from nomad_simulations.schema_packages.properties import ElectronicBandGap
outputs = Outputs()
outputs.electronic_band_gaps.append(ElectronicBandGap(value=2.0 * ureg.eV))
outputs.electronic_band_gaps.append(
ElectronicBandGap(value=1.8 * ureg.eV, spin_channel=0)
)
outputs.electronic_band_gaps.append(
ElectronicBandGap(value=1.9 * ureg.eV, spin_channel=1)
)
spin_polarized = outputs.extract_spin_polarized_property('electronic_band_gaps')
assert len(outputs.electronic_band_gaps) == 3
assert len(spin_polarized) == 2
assert {gap.spin_channel for gap in spin_polarized} == {0, 1}
About variable-dependent properties
The old generic variables pattern previously shown for ElectronicBandGap is not
the current design. Variable grids are now represented by property-specific sections
(for example DOS/ spectra energy grids).
Normalization Exercises¶
The following exercises focus on practical normalization behavior from current tests.
Exercise N1: representative ModelSystem normalization¶
Exercise N1
Normalize a representative atomic ModelSystem and confirm that derived
fields (type, chemical_formula) are populated.
Solution N1
import numpy as np
from nomad import utils
from nomad.datamodel import EntryArchive
from nomad.units import ureg
from nomad_simulations.schema_packages.atoms_state import AtomsState
from nomad_simulations.schema_packages.model_system import ModelSystem
logger = utils.get_logger(__name__)
# Pattern based on tests/test_model_system.py normalization behavior.
model_system = ModelSystem(is_representative=True)
model_system.lattice_vectors = np.eye(3) * 5.43 * ureg.angstrom
model_system.periodic_boundary_conditions = [True, True, True]
model_system.positions = (
np.array([[0.0, 0.0, 0.0], [1.3575, 1.3575, 1.3575]]) * ureg.angstrom
)
model_system.particle_states.append(AtomsState(chemical_symbol='Si'))
model_system.particle_states.append(AtomsState(chemical_symbol='Si'))
model_system.normalize(EntryArchive(), logger)
assert model_system.type == 'bulk'
assert model_system.chemical_formula is not None
assert model_system.chemical_formula.reduced == 'Si'
Exercise N2: Outputs SCF deltas from total energies¶
Exercise N2
Populate only total_energies and run Outputs.normalize(...) to confirm
that scf_steps.delta_energies_total is computed automatically.
Solution N2
import numpy as np
from nomad import utils
from nomad.datamodel import EntryArchive
from nomad.units import ureg
from nomad_simulations.schema_packages.outputs import Outputs, SCFSteps
from nomad_simulations.schema_packages.properties import TotalEnergy
logger = utils.get_logger(__name__)
# Pattern based on tests/workflow/test_convergence_targets.py.
outputs = Outputs(scf_steps=SCFSteps())
outputs.total_energies.append(TotalEnergy(value=1.0 * ureg.eV))
outputs.total_energies.append(TotalEnergy(value=1.5 * ureg.eV))
outputs.total_energies.append(TotalEnergy(value=2.1 * ureg.eV))
outputs.normalize(EntryArchive(), logger)
assert outputs.scf_steps is not None
assert outputs.scf_steps.delta_energies_total is not None
assert len(outputs.scf_steps.delta_energies_total) == 2
expected_first = (0.5 * ureg.eV).to('joule').magnitude
assert np.isclose(
outputs.scf_steps.delta_energies_total[0].to('joule').magnitude, expected_first
)
For full normalization execution-order rules, see Normalization.