def __init__(self, omp=os.environ.get('OMP_NUM_THREADS', '1'), mpi_run=os.environ.get('BIGDFT_MPIRUN', ''), dry_run=False, skip=False, code="bigdft@localhost", verbose=True, **kwargs): Runner.__init__(self, omp=str(omp), mpi_run=mpi_run, dry_run=dry_run, skip=skip, verbose=verbose, **kwargs) # Build the command setting the number of omp threads self.code = load_code(code) self.job = BigDFTCalcJob # we don't want the executable and such self.command = "" # ignore this for datasets, as aiida splits each # computation in its own folder self.run_dir = "." # store logfiles and names in order to # skip execution if necessary. self.logfiles = {} # store output information on all runs. self.outputs = {} safe_print( 'Initialize an Aiida Calculator for %s with %d machine(s)' % ((self.code), self._global_options.get('num_machines', 1)), ', %d processes per machine, and %s cores per process' % ((self._global_options.get('mpiprocs_per_machine', 1), self._global_options['omp'])))
def relaunch_cached(results): """Launch the same calculations but with caching enabled -- these should be FINISHED immediately.""" code_doubler = load_code(CODENAME_DOUBLER) cached_calcs = [] with enable_caching(identifier='aiida.calculations:templatereplacer'): for counter in range(1, NUMBER_CALCULATIONS + 1): inputval = counter calc, expected_result = run_calculation(code=code_doubler, counter=counter, inputval=inputval) cached_calcs.append(calc) results['calculations'][calc.pk] = expected_result if not (validate_calculations(results['calculations']) and validate_workchains(results['workchains']) and validate_cached(cached_calcs) and validate_process_functions(results['process_functions'])): print_daemon_log() print('') print( 'ERROR! Some return values are different from the expected value') sys.exit(3) print_daemon_log() print('') print('OK, all calculations have the expected parsed result')
def _load_code(code): if code is not None: try: return load_code(code) except NotExistent as error: print("error", error) return None
def generate_inputs(process_class: engine.Process, protocol: Dict, code: orm.Code, structure: StructureData, override: Dict[str, Any] = None) -> Dict[str, Any]: """Generate the input parameters for the given workchain type for a given code and structure. The override argument can be used to pass a dictionary with values for specific inputs that should override the defaults. This dictionary should have the same nested structure as the final input dictionary would have for the workchain submission. :param process_class: process class, either calculation or workchain, i.e. ``AbinitCalculation`` or ``AbinitBaseWorkChain`` :param protocol: the protocol based on which to choose input parameters :param code: the code or code name to use :param magnetism: the type of magnetisation to be used :param initial_mag: value for the initial magnetisation along the z axis. :param soc: whether or not to use spin-orbit coupling :param structure: the structure :param override: a dictionary to override specific inputs :return: input dictionary """ # pylint: disable=too-many-arguments,unused-argument from aiida.common.lang import type_check AbinitCalculation = plugins.CalculationFactory('abinit') # pylint: disable=invalid-name AbinitBaseWorkChain = plugins.WorkflowFactory('abinit.base') # pylint: disable=invalid-name type_check(structure, orm.StructureData) if not isinstance(code, orm.Code): try: code = orm.load_code(code) except (exceptions.MultipleObjectsError, exceptions.NotExistent) as exception: raise ValueError('could not load the code {}: {}'.format( code, exception)) if process_class == AbinitCalculation: protocol = protocol['abinit'] dictionary = generate_inputs_calculation(protocol, code, structure, override) elif process_class == AbinitBaseWorkChain: protocol = protocol['base'] dictionary = generate_inputs_base(protocol, code, structure, override) else: raise NotImplementedError( 'process class {} is not supported'.format(process_class)) return dictionary
def run_base_restart_workchain(): """Run the `AddArithmeticBaseWorkChain` a few times for various inputs.""" code = load_code(CODENAME_ADD) inputs = { 'add': { 'x': Int(1), 'y': Int(2), 'code': code, 'settings': Dict(dict={'allow_negative': False}), 'metadata': { 'options': { 'resources': { 'num_machines': 1, 'num_mpiprocs_per_machine': 1 } } } } } # Normal inputs should run just fine results, node = run.get_node(ArithmeticAddBaseWorkChain, **inputs) assert node.is_finished_ok, node.exit_status assert len(node.called) == 1 assert 'sum' in results assert results['sum'].value == 3 # With one input negative, the sum will be negative which will fail the calculation, but the error handler should # fix it, so the second calculation should finish successfully inputs['add']['y'] = Int(-4) results, node = run.get_node(ArithmeticAddBaseWorkChain, **inputs) assert node.is_finished_ok, node.exit_status assert len(node.called) == 2 assert 'sum' in results assert results['sum'].value == 5 # The silly sanity check aborts the workchain if the sum is bigger than 10 inputs['add']['y'] = Int(10) results, node = run.get_node(ArithmeticAddBaseWorkChain, **inputs) assert not node.is_finished_ok, node.process_state assert node.exit_status == ArithmeticAddBaseWorkChain.exit_codes.ERROR_TOO_BIG.status, node.exit_status # pylint: disable=no-member assert len(node.called) == 1 # Check that overriding default handler enabled status works inputs['add']['y'] = Int(1) inputs['handler_overrides'] = Dict(dict={'disabled_handler': True}) results, node = run.get_node(ArithmeticAddBaseWorkChain, **inputs) assert not node.is_finished_ok, node.process_state assert node.exit_status == ArithmeticAddBaseWorkChain.exit_codes.ERROR_ENABLED_DOOM.status, node.exit_status # pylint: disable=no-member assert len(node.called) == 1
def __init__(self, pw_code_id: ty.Union[str, int], structure_group_id: ty.Union[str, int], pseudo_family_id: ty.Union[str, int], *args, structure_filters: ty.Optional[ty.Dict[str, ty.Any]] = None, **kwargs): """A SubmissionController for PwBaseWorkChains.""" super().__init__(*args, **kwargs) self._code = orm.load_code(identifier=pw_code_id) self._process_class = plugins.WorkflowFactory( self.WORKFLOW_ENTRY_POINT) self._structure_group = orm.load_group(identifier=structure_group_id) self._structure_filters = structure_filters if structure_filters is not None else {} self._pseudo_family = orm.load_group(identifier=pseudo_family_id)
def run_arithmetic_add(): """Run the `ArithmeticAddCalculation`.""" ArithmeticAddCalculation = CalculationFactory('arithmetic.add') code = load_code(CODENAME_ADD) inputs = { 'x': Int(1), 'y': Int(2), 'code': code, } # Normal inputs should run just fine results, node = run.get_node(ArithmeticAddCalculation, **inputs) assert node.is_finished_ok, node.exit_status assert results['sum'] == 3
def run_workchain( number=1, code="sleep@slurm", time=1, payload=100, output_dict=100, output_array=100, fail=False, submit=False, ): """Run the `SleepWorkChain` :param number: Number of children `SleepCalculation` :param code: code label :param time: seconds for which each `SleepCalculation` runs `sleep` :param payload: number of fields in `payload` input dictionary of CalcJob :param output: number of fields in output dictionary of CalcJob :param output_array: Size of output array :param fail: Intentionally fail all `SleepCalculation` :param submit: whether to submit to daemon, otherwise run :return: workchain node """ from aiida.engine import run_get_node from aiida.engine import submit as submit_func from aiida.orm import load_code from aiida.plugins import WorkflowFactory builder = WorkflowFactory("sleep").get_builder() builder.children = number builder.calcjob.code = load_code(code) builder.calcjob.time = time builder.calcjob.payload = { f"input_key_{i}": f"value_{i}" for i in range(payload) } builder.calcjob.metadata.options.fail_calcjob = fail builder.calcjob.metadata.options.output_dict_size = output_dict builder.calcjob.metadata.options.output_array_size = output_array if submit: node = submit_func(builder) else: node = run_get_node(builder).node return node
def get_builder( self, structure, calc_engines, protocol, relaxation_type, threshold_forces=None, threshold_stress=None, previous_workchain=None, **kwargs ): super().get_builder( structure, calc_engines, protocol, relaxation_type, threshold_forces, threshold_stress, previous_workchain, **kwargs ) from aiida.orm import Dict from aiida.orm import load_code if relaxation_type == RelaxType.ATOMS: relaxation_schema = 'relax' else: raise ValueError('relaxation type `{}` is not supported'.format(relaxation_type.value)) builder = self.process_class.get_builder() builder.structure = structure # Will be implemented in the bigdft plugin # inputdict = BigDFTParameters.get_input_dict(protocol, structure, 'relax') # for now apply simple stupid heuristic : atoms < 200 -> cubic, else -> linear. if len(structure.sites) <= 200: inputdict = self.get_protocol(protocol)['inputdict_cubic'] else: inputdict = self.get_protocol(protocol)['inputdict_linear'] builder.parameters = BigDFTParameters(dict=inputdict) builder.code = load_code(calc_engines[relaxation_schema]['code']) run_opts = {'options': calc_engines[relaxation_schema]['options']} builder.run_opts = Dict(dict=run_opts) if threshold_forces is not None: builder.relax.threshold_forces = orm.Float(threshold_forces) return builder
def run_multiply_add_workchain(): """Run the `MultiplyAddWorkChain`.""" MultiplyAddWorkChain = WorkflowFactory('arithmetic.multiply_add') code = load_code(CODENAME_ADD) inputs = { 'x': Int(1), 'y': Int(2), 'z': Int(3), 'code': code, } # Normal inputs should run just fine results, node = run.get_node(MultiplyAddWorkChain, **inputs) assert node.is_finished_ok, node.exit_status assert len(node.called) == 2 assert 'result' in results assert results['result'].value == 5
def main(): # Log to the console console = logging.StreamHandler() console.setFormatter(logging.Formatter("[%(levelname)s] %(name)s : %(message)s")) logging.getLogger("add").addHandler(console) logging.getLogger("add").setLevel(logging.DEBUG) code = load_code(label="add@localhost") calculation = CalculationFactory("add.calculation") builder = calculation.get_builder() builder.code = code builder.x = Float(3.0) builder.y = Float(3.5) builder.metadata.options = {"resources": {"num_machines": 1}} results, node = run.get_node(builder) print(results, node, sep="\n")
def run_calc( code="sleep@slurm", time=1, payload=100, output_dict=100, output_array=100, fail=False, submit=False, trace=False, ): """Run the `SleepCalculation` :param code: code label :param time: time the `SleepCalculation` runs sleep for (seconds) :param payload: Size of input dict :param output_dict: Size of output dict :param output_array: Size of output array :param fail: Intentionally fail the `SleepCalculation` :param submit: whether to submit to daemon, otherwise run :return: workchain node """ from aiida.engine import run_get_node from aiida.engine import submit as submit_func from aiida.orm import load_code builder = load_code(code).get_builder() builder.time = time builder.payload = {f"input_key_{i}": f"value_{i}" for i in range(payload)} builder.metadata.options.fail_calcjob = fail builder.metadata.options.output_dict_size = output_dict builder.metadata.options.output_array_size = output_array if submit: node = submit_func(builder) else: node = run_get_node(builder).node return node
def get_builder( self, structure: StructureData, calc_engines: Dict[str, Any], protocol, relaxation_type: RelaxType, threshold_forces: float = None, threshold_stress: float = None, previous_workchain=None, **kwargs) -> engine.ProcessBuilder: # pylint: disable=too-many-locals """Return a process builder for the corresponding workchain class with inputs set according to the protocol. :param structure: the structure to be relaxed :param calc_engines: ... :param protocol: the protocol to use when determining the workchain inputs :param relaxation_type: the type of relaxation to perform, instance of `RelaxType` :param threshold_forces: target threshold for the forces in eV/Å. :param threshold_stress: target threshold for the stress in eV/Å^3. :return: a `aiida.engine.processes.ProcessBuilder` instance ready to be submitted. """ super().get_builder(structure, calc_engines, protocol, relaxation_type, threshold_forces, threshold_stress, previous_workchain, **kwargs) # The builder. builder = self.process_class.get_builder() # Switch on the resubmit_unconverged_geometry which is disabled by default. builder.handler_overrides = orm.Dict( dict={'resubmit_unconverged_geometry': True}) builder.cp2k.file = get_file_section() # Input structure. builder.cp2k.structure = structure # Input parameters. parameters = self.get_protocol(protocol) ## Removing description. _ = parameters.pop('description') kinds_section = get_kinds_section(builder.cp2k.structure) dict_merge(parameters, kinds_section) ## Relaxation type. if relaxation_type == RelaxType.ATOMS: run_type = 'GEO_OPT' elif relaxation_type == RelaxType.ATOMS_CELL: run_type = 'CELL_OPT' else: raise ValueError('relaxation type `{}` is not supported'.format( relaxation_type.value)) parameters['GLOBAL'] = {'RUN_TYPE': run_type} ## Redefining forces threshold. if threshold_forces is not None: parameters['MOTION'][run_type][ 'MAX_FORCE'] = '[eV/angstrom] {}'.format(threshold_forces) ## Redefining stress threshold. if threshold_stress is not None: parameters['MOTION']['CELL_OPT'][ 'PRESSURE_TOLERANCE'] = '[GPa] {}'.format(threshold_stress * EV_A3_TO_GPA) builder.cp2k.parameters = orm.Dict(dict=parameters) # Additional files to be retrieved. builder.cp2k.settings = orm.Dict(dict={ 'additional_retrieve_list': ['aiida-frc-1.xyz', 'aiida-1.stress'] }) # CP2K code. builder.cp2k.code = orm.load_code(calc_engines['relax']['code']) # Run options. builder.cp2k.metadata.options = calc_engines['relax']['options'] return builder
def get_builder(self, structure: StructureData, calc_engines: Dict[str, Any], *, protocol: str = None, relax_type: RelaxType = RelaxType.ATOMS, electronic_type: ElectronicType = ElectronicType.METAL, spin_type: SpinType = SpinType.NONE, magnetization_per_site: List[float] = None, threshold_forces: float = None, threshold_stress: float = None, previous_workchain=None, **kwargs) -> engine.ProcessBuilder: """Return a process builder for the corresponding workchain class with inputs set according to the protocol. :param structure: the structure to be relaxed. :param calc_engines: a dictionary containing the computational resources for the relaxation. :param protocol: the protocol to use when determining the workchain inputs. :param relax_type: the type of relaxation to perform. :param electronic_type: the electronic character that is to be used for the structure. :param spin_type: the spin polarization type to use for the calculation. :param magnetization_per_site: a list with the initial spin polarization for each site. Float or integer in units of electrons. If not defined, the builder will automatically define the initial magnetization if and only if `spin_type != SpinType.NONE`. :param threshold_forces: target threshold for the forces in eV/Å. :param threshold_stress: target threshold for the stress in eV/Å^3. :param previous_workchain: a <Code>RelaxWorkChain node. :param kwargs: any inputs that are specific to the plugin. :return: a `aiida.engine.processes.ProcessBuilder` instance ready to be submitted. """ # pylint: disable=too-many-locals protocol = protocol or self.get_default_protocol_name() super().get_builder(structure, calc_engines, protocol=protocol, relax_type=relax_type, electronic_type=electronic_type, spin_type=spin_type, magnetization_per_site=magnetization_per_site, threshold_forces=threshold_forces, threshold_stress=threshold_stress, previous_workchain=previous_workchain, **kwargs) # The builder. builder = self.process_class.get_builder() # Input parameters. parameters = self.get_protocol(protocol) ## Kpoints. kpoints_distance = parameters.pop('kpoints_distance', None) kpoints = self._get_kpoints(kpoints_distance, structure, previous_workchain) mesh, _ = kpoints.get_kpoints_mesh() if mesh != [1, 1, 1]: builder.cp2k.kpoints = kpoints ## Removing description. _ = parameters.pop('description') magnetization_tags = None ## Metal or insulator. if electronic_type == ElectronicType.METAL: parameters['FORCE_EVAL']['DFT']['SCF']['SMEAR'] = { '_': 'ON', 'METHOD': 'FERMI_DIRAC', 'ELECTRONIC_TEMPERATURE': '[K] 300' } parameters['FORCE_EVAL']['DFT']['SCF']['ADDED_MOS'] = 100 ## Magnetic calculation if spin_type == SpinType.NONE: parameters['FORCE_EVAL']['DFT']['UKS'] = False if magnetization_per_site is not None: import warnings warnings.warn( '`magnetization_per_site` will be ignored as `spin_type` is set to SpinType.NONE' ) elif spin_type == SpinType.COLLINEAR: parameters['FORCE_EVAL']['DFT']['UKS'] = True structure, magnetization_tags = tags_and_magnetization( structure, magnetization_per_site) parameters['FORCE_EVAL']['DFT'][ 'MULTIPLICITY'] = guess_multiplicity(structure, magnetization_per_site) ## Starting magnetization. kinds_section = get_kinds_section(structure, magnetization_tags) dict_merge(parameters, kinds_section) ## Relaxation type. if relax_type == RelaxType.ATOMS: run_type = 'GEO_OPT' elif relax_type == RelaxType.ATOMS_CELL: run_type = 'CELL_OPT' else: raise ValueError( f'Relax type `{relax_type.value}` is not supported') parameters['GLOBAL'] = {'RUN_TYPE': run_type} ## Redefining forces threshold. if threshold_forces is not None: parameters['MOTION'][run_type][ 'MAX_FORCE'] = f'[eV/angstrom] {threshold_forces}' ## Redefining stress threshold. if threshold_stress is not None: parameters['MOTION']['CELL_OPT'][ 'PRESSURE_TOLERANCE'] = f'[GPa] {threshold_stress * EV_A3_TO_GPA}' builder.cp2k.parameters = orm.Dict(dict=parameters) # Switch on the resubmit_unconverged_geometry which is disabled by default. builder.handler_overrides = orm.Dict( dict={'resubmit_unconverged_geometry': True}) # Files. builder.cp2k.file = get_file_section() # Input structure. builder.cp2k.structure = structure # Additional files to be retrieved. builder.cp2k.settings = orm.Dict(dict={ 'additional_retrieve_list': ['aiida-frc-1.xyz', 'aiida-1.stress'] }) # CP2K code. builder.cp2k.code = orm.load_code(calc_engines['relax']['code']) # Run options. builder.cp2k.metadata.options = calc_engines['relax']['options'] # Use advanced parser to parse more data. builder.cp2k.metadata.options['parser_name'] = 'cp2k_advanced_parser' return builder
def get_builder( # pylint: disable=too-many-branches, too-many-statements self, structure: StructureData, engines: Dict[str, Any], *, protocol: str = None, relax_type: RelaxType = RelaxType.POSITIONS, electronic_type: ElectronicType = ElectronicType.METAL, spin_type: SpinType = SpinType.NONE, magnetization_per_site: List[float] = None, threshold_forces: float = None, threshold_stress: float = None, reference_workchain=None, **kwargs) -> engine.ProcessBuilder: """Return a process builder for the corresponding workchain class with inputs set according to the protocol. :param structure: the structure to be relaxed. :param engines: a dictionary containing the computational resources for the relaxation. :param protocol: the protocol to use when determining the workchain inputs. :param relax_type: the type of relaxation to perform. :param electronic_type: the electronic character that is to be used for the structure. :param spin_type: the spin polarization type to use for the calculation. :param magnetization_per_site: a list with the initial spin polarization for each site. Float or integer in units of electrons. If not defined, the builder will automatically define the initial magnetization if and only if `spin_type != SpinType.NONE`. :param threshold_forces: target threshold for the forces in eV/Å. :param threshold_stress: target threshold for the stress in eV/Å^3. :param reference_workchain: a <Code>RelaxWorkChain node. :param kwargs: any inputs that are specific to the plugin. :return: a `aiida.engine.processes.ProcessBuilder` instance ready to be submitted. """ # pylint: disable=too-many-locals protocol = protocol or self.get_default_protocol_name() super().get_builder(structure, engines, protocol=protocol, relax_type=relax_type, electronic_type=electronic_type, spin_type=spin_type, magnetization_per_site=magnetization_per_site, threshold_forces=threshold_forces, threshold_stress=threshold_stress, reference_workchain=reference_workchain, **kwargs) # Checks if any(structure.get_attribute_many(['pbc1', 'pbc2', 'pbc2'])): warnings.warn( 'Warning: PBC detected in the input structure. It is not supported and thus is ignored.' ) if protocol not in self.get_protocol_names(): warnings.warn( 'no protocol implemented with name {}, using default moderate'. format(protocol)) protocol = self.get_default_protocol_name() if 'relax' not in engines: raise ValueError( 'The `engines` dictionaly must contain "relax" as outermost key' ) params = self._get_params(protocol) # Delete optimization related keywords if it is a single point calculation if relax_type == RelaxType.NONE: inp_keywords = deepcopy(params['input_keywords']) new_inp_keywords = [] for item in inp_keywords: if 'opt' not in item.lower(): new_inp_keywords.append(item) new_inp_keywords.append('EnGrad') params['input_keywords'] = new_inp_keywords # Handle charge and multiplicity strc_pmg = structure.get_pymatgen_molecule() num_electrons = strc_pmg.nelectrons if num_electrons % 2 == 1 and spin_type == SpinType.NONE: raise ValueError( f'Spin-restricted calculation does not support odd number of electrons ({num_electrons})' ) params['charge'] = int(strc_pmg.charge) spin_multiplicity = 1 # Logic from Kristijan code in gaussian. if spin_type == SpinType.COLLINEAR: params['input_keywords'].append('UKS') if magnetization_per_site is None: multiplicity_guess = 1 else: warnings.warn( 'Warning: magnetization_per_site site-resolved info is disregarded, only total spin is processed.' ) # magnetization_per_site are in units of [Bohr magnetons] (*0.5 to get in [au]) total_spin_guess = 0.5 * np.abs(np.sum(magnetization_per_site)) multiplicity_guess = 2 * total_spin_guess + 1 # in case of even/odd electrons, find closest odd/even multiplicity if num_electrons % 2 == 0: # round guess to nearest odd integer spin_multiplicity = int( np.round((multiplicity_guess - 1) / 2) * 2 + 1) else: # round guess to nearest even integer; 0 goes to 2 spin_multiplicity = max( [int(np.round(multiplicity_guess / 2) * 2), 2]) if spin_multiplicity == 1: params['input_blocks']['scf']['STABPerform'] = True if 'EnGrad' in params['input_keywords']: params['input_keywords'].remove('EnGrad') params['multiplicity'] = spin_multiplicity # Handle resources resources = engines['relax']['options']['resources'] nproc = None if 'tot_num_mpiprocs' in resources: nproc = resources['tot_num_mpiprocs'] elif 'num_machines' in resources: if 'num_mpiprocs_per_machine' in resources: nproc = resources['num_machines'] * resources[ 'num_mpiprocs_per_machine'] else: code = orm.load_code(engines['relax']['code']) default_mpiprocs = code.computer.get_default_mpiprocs_per_machine( ) if default_mpiprocs is not None: nproc = resources['num_machines'] * default_mpiprocs if nproc is not None: params['input_blocks']['pal'] = {'nproc': nproc} builder = self.process_class.get_builder() builder.orca.structure = structure builder.orca.parameters = orm.Dict(dict=params) builder.orca.code = orm.load_code(engines['relax']['code']) builder.orca.metadata.options = engines['relax']['options'] return builder
def get_builder( # pylint: disable=too-many-branches, too-many-locals self, structure: StructureData, calc_engines: Dict[str, Any], *, protocol: str = None, relax_type: RelaxType = RelaxType.ATOMS, electronic_type: ElectronicType = ElectronicType.METAL, spin_type: SpinType = SpinType.NONE, magnetization_per_site: List[float] = None, threshold_forces: float = None, threshold_stress: float = None, previous_workchain=None, **kwargs) -> engine.ProcessBuilder: """ Return a process builder for the corresponding workchain class with inputs set according to the protocol. :param structure: the structure to be relaxed. :param calc_engines: a dictionary containing the computational resources for the relaxation. :param protocol: the protocol to use when determining the workchain inputs. :param relax_type: the type of relaxation to perform. :param electronic_type: the electronic character that is to be used for the structure. :param spin_type: the spin polarization type to use for the calculation. :param magnetization_per_site: a list with the initial spin polarization for each site. Float or integer in units of electrons. If not defined, the builder will automatically define the initial magnetization if and only if `spin_type != SpinType.NONE`. :param threshold_forces: target threshold for the forces in eV/Å. :param threshold_stress: target threshold for the stress in eV/Å^3. :param previous_workchain: a <Code>RelaxWorkChain node. :param kwargs: any inputs that are specific to the plugin. :return: a `aiida.engine.processes.ProcessBuilder` instance ready to be submitted. """ protocol = protocol or self.get_default_protocol_name() super().get_builder(structure, calc_engines, protocol=protocol, relax_type=relax_type, electronic_type=electronic_type, spin_type=spin_type, magnetization_per_site=magnetization_per_site, threshold_forces=threshold_forces, threshold_stress=threshold_stress, previous_workchain=previous_workchain, **kwargs) # Checks if protocol not in self.get_protocol_names(): import warnings warnings.warn( 'no protocol implemented with name {}, using default moderate'. format(protocol)) protocol = self.get_default_protocol_name() if 'relaxation' not in calc_engines: raise ValueError( 'The `calc_engines` dictionaly must contain "relaxation" as outermost key' ) pseudo_family = self._protocols[protocol]['pseudo_family'] try: orm.Group.objects.get(label=pseudo_family) except exceptions.NotExistent: raise ValueError( 'protocol `{}` requires `pseudo_family` with name {} ' 'but no family with this name is loaded in the database'. format(protocol, pseudo_family)) # K points kpoints_mesh = self._get_kpoints(protocol, structure, previous_workchain) # Parameters, including scf ... parameters = self._get_param(protocol, structure) #... relax options ... if relax_type != RelaxType.NONE: parameters['md-type-of-run'] = 'cg' parameters['md-num-cg-steps'] = 100 if relax_type == RelaxType.ATOMS_CELL: parameters['md-variable-cell'] = True if threshold_forces: parameters['md-max-force-tol'] = str(threshold_forces) + ' eV/Ang' if threshold_stress: parameters['md-max-stress-tol'] = str( threshold_stress) + ' eV/Ang**3' #... spin options (including initial magentization) ... if spin_type == SpinType.COLLINEAR: parameters['spin'] = 'polarized' if magnetization_per_site is not None: if spin_type == SpinType.NONE: import warnings warnings.warn( '`magnetization_per_site` will be ignored as `spin_type` is set to SpinType.NONE' ) if spin_type == SpinType.COLLINEAR: in_spin_card = '\n' for i, magn in enumerate(magnetization_per_site): in_spin_card += f' {i+1} {magn} \n' in_spin_card += '%endblock dm-init-spin' parameters['%block dm-init-spin'] = in_spin_card # Basis basis = self._get_basis(protocol, structure) # Pseudo fam pseudo_family = self._get_pseudo_fam(protocol) builder = self.process_class.get_builder() builder.structure = structure builder.basis = orm.Dict(dict=basis) builder.parameters = orm.Dict(dict=parameters) if kpoints_mesh: builder.kpoints = kpoints_mesh builder.pseudo_family = pseudo_family builder.options = orm.Dict(dict=calc_engines['relaxation']['options']) builder.code = orm.load_code(calc_engines['relaxation']['code']) return builder
def get_builder( self, structure: StructureData, calc_engines: Dict[str, Any], *, protocol: str = None, relax_type: RelaxType = RelaxType.ATOMS, electronic_type: ElectronicType = ElectronicType.METAL, spin_type: SpinType = SpinType.NONE, magnetization_per_site: List[float] = None, threshold_forces: float = None, threshold_stress: float = None, previous_workchain=None, **kwargs ) -> engine.ProcessBuilder: """Return a process builder for the corresponding workchain class with inputs set according to the protocol. :param structure: the structure to be relaxed. :param calc_engines: a dictionary containing the computational resources for the relaxation. :param protocol: the protocol to use when determining the workchain inputs. :param relax_type: the type of relaxation to perform. :param electronic_type: the electronic character that is to be used for the structure. :param spin_type: the spin polarization type to use for the calculation. :param magnetization_per_site: a list with the initial spin polarization for each site. Float or integer in units of electrons. If not defined, the builder will automatically define the initial magnetization if and only if `spin_type != SpinType.NONE`. :param threshold_forces: target threshold for the forces in eV/Å. :param threshold_stress: target threshold for the stress in eV/Å^3. :param previous_workchain: a <Code>RelaxWorkChain node. :param kwargs: any inputs that are specific to the plugin. :return: a `aiida.engine.processes.ProcessBuilder` instance ready to be submitted. """ # pylint: disable=too-many-locals,too-many-branches,too-many-statements protocol = protocol or self.get_default_protocol_name() super().get_builder( structure, calc_engines, protocol=protocol, relax_type=relax_type, electronic_type=electronic_type, spin_type=spin_type, magnetization_per_site=magnetization_per_site, threshold_forces=threshold_forces, threshold_stress=threshold_stress, previous_workchain=previous_workchain, **kwargs ) if any(structure.get_attribute_many(['pbc1', 'pbc2', 'pbc3'])): print('Warning: PBC detected in input structure. It is not supported and thus ignored.') # ----------------------------------------------------------------- # Set the link0 memory and n_proc based on the calc_engines options dict link0_parameters = {'%chk': 'aiida.chk'} options = calc_engines['relax']['options'] res = options['resources'] if 'max_memory_kb' not in options: # If memory is not set, set a default of 2 GB link0_parameters['%mem'] = '2048MB' else: # If memory is set, specify 80% of it to gaussian link0_parameters['%mem'] = '%dMB' % ((0.8 * options['max_memory_kb']) // 1024) # Determine the number of processors that should be specified to Gaussian n_proc = None if 'tot_num_mpiprocs' in res: n_proc = res['tot_num_mpiprocs'] elif 'num_machines' in res: if 'num_mpiprocs_per_machine' in res: n_proc = res['num_machines'] * res['num_mpiprocs_per_machine'] else: code = load_code(calc_engines['relax']['code']) def_mppm = code.computer.get_default_mpiprocs_per_machine() if def_mppm is not None: n_proc = res['num_machines'] * def_mppm if n_proc is not None: link0_parameters['%nprocshared'] = '%d' % n_proc # ----------------------------------------------------------------- # General route parameters sel_protocol = copy.deepcopy(self.get_protocol(protocol)) route_params = sel_protocol['route_parameters'] if relax_type == RelaxType.NONE: del route_params['opt'] route_params['force'] = None if threshold_forces is not None: # Set the RMS force threshold with the iop(1/7=N) command # threshold = N * 10**(-6) in [EH/Bohr] threshold_forces_au = threshold_forces * EV_TO_EH / ANG_TO_BOHR if threshold_forces_au < 1e-6: print('Warning: Forces threshold cannot be lower than 1e-6 au.') threshold_forces_au = 1e-6 threshold_forces_n = int(np.round(threshold_forces_au * 1e6)) route_params['iop(1/7=%d)' % threshold_forces_n] = None # ----------------------------------------------------------------- # Handle spin-polarization pymatgen_structure = structure.get_pymatgen_molecule() num_electrons = pymatgen_structure.nelectrons spin_multiplicity = 1 if num_electrons % 2 == 1 and spin_type == SpinType.NONE: raise ValueError(f'Spin-restricted calculation does not support odd number of electrons ({num_electrons})') if spin_type == SpinType.COLLINEAR: # enable UKS sel_protocol['functional'] = 'U' + sel_protocol['functional'] # determine the spin multiplicity if magnetization_per_site is None: multiplicity_guess = 1 else: print( 'Warning: magnetization_per_site site-resolved info is disregarded, only total spin is processed.' ) # magnetization_per_site are in units of Bohr magnetons total_spin_guess = np.abs(np.sum(magnetization_per_site)) multiplicity_guess = 2 * total_spin_guess + 1 # in case of even/odd electrons, find closest odd/even multiplicity if num_electrons % 2 == 0: # round guess to nearest odd integer spin_multiplicity = int(np.round((multiplicity_guess - 1) / 2) * 2 + 1) else: # round guess to nearest even integer; 0 goes to 2 spin_multiplicity = max([int(np.round(multiplicity_guess / 2) * 2), 2]) # Mix H**O and LUMO if we're looking for the open-shell singlet if spin_multiplicity == 1: route_params['guess'] = 'mix' # ----------------------------------------------------------------- # Build the builder params = { 'link0_parameters': link0_parameters, 'functional': sel_protocol['functional'], 'basis_set': sel_protocol['basis_set'], 'charge': 0, 'multiplicity': spin_multiplicity, 'route_parameters': route_params } builder = self.process_class.get_builder() builder.gaussian.structure = structure builder.gaussian.parameters = orm.Dict(dict=params) builder.gaussian.code = orm.load_code(calc_engines['relax']['code']) builder.gaussian.metadata.options = calc_engines['relax']['options'] return builder
def get_builder_from_protocol(cls, code, structure, protocol=None, overrides=None, electronic_type=ElectronicType.METAL, spin_type=SpinType.NONE, initial_magnetic_moments=None, **_): """Return a builder prepopulated with inputs selected according to the chosen protocol. :param code: the ``Code`` instance configured for the ``quantumespresso.pw`` plugin. :param structure: the ``StructureData`` instance to use. :param protocol: protocol to use, if not specified, the default will be used. :param overrides: optional dictionary of inputs to override the defaults of the protocol. :param electronic_type: indicate the electronic character of the system through ``ElectronicType`` instance. :param spin_type: indicate the spin polarization type to use through a ``SpinType`` instance. :param initial_magnetic_moments: optional dictionary that maps the initial magnetic moment of each kind to a desired value for a spin polarized calculation. Note that for ``spin_type == SpinType.COLLINEAR`` an initial guess for the magnetic moment is automatically set in case this argument is not provided. :return: a process builder instance with all inputs defined ready for launch. """ from aiida_quantumespresso.workflows.protocols.utils import get_starting_magnetization if isinstance(code, str): code = orm.load_code(code) type_check(code, orm.Code) type_check(electronic_type, ElectronicType) type_check(spin_type, SpinType) if electronic_type not in [ ElectronicType.METAL, ElectronicType.INSULATOR ]: raise NotImplementedError( f'electronic type `{electronic_type}` is not supported.') if spin_type not in [SpinType.NONE, SpinType.COLLINEAR]: raise NotImplementedError( f'spin type `{spin_type}` is not supported.') if initial_magnetic_moments is not None and spin_type is not SpinType.COLLINEAR: raise ValueError( f'`initial_magnetic_moments` is specified but spin type `{spin_type}` is incompatible.' ) inputs = cls.get_protocol_inputs(protocol, overrides) meta_parameters = inputs.pop('meta_parameters') pseudo_family = inputs.pop('pseudo_family') natoms = len(structure.sites) try: pseudo_set = (PseudoDojoFamily, SsspFamily, CutoffsPseudoPotentialFamily) pseudo_family = orm.QueryBuilder().append(pseudo_set, filters={ 'label': pseudo_family }).one()[0] except exceptions.NotExistent as exception: raise ValueError( f'required pseudo family `{pseudo_family}` is not installed. Please use `aiida-pseudo install` to' 'install it.') from exception try: cutoff_wfc, cutoff_rho = pseudo_family.get_recommended_cutoffs( structure=structure, unit='Ry') except ValueError as exception: raise ValueError( f'failed to obtain recommended cutoffs for pseudo family `{pseudo_family}`: {exception}' ) from exception parameters = inputs['pw']['parameters'] parameters['CONTROL']['etot_conv_thr'] = natoms * meta_parameters[ 'etot_conv_thr_per_atom'] parameters['ELECTRONS'][ 'conv_thr'] = natoms * meta_parameters['conv_thr_per_atom'] parameters['SYSTEM']['ecutwfc'] = cutoff_wfc parameters['SYSTEM']['ecutrho'] = cutoff_rho if electronic_type is ElectronicType.INSULATOR: parameters['SYSTEM']['occupations'] = 'fixed' parameters['SYSTEM'].pop('degauss') parameters['SYSTEM'].pop('smearing') if spin_type is SpinType.COLLINEAR: starting_magnetization = get_starting_magnetization( structure, pseudo_family, initial_magnetic_moments) parameters['SYSTEM']['nspin'] = 2 parameters['SYSTEM'][ 'starting_magnetization'] = starting_magnetization # pylint: disable=no-member builder = cls.get_builder() builder.pw['code'] = code builder.pw['pseudos'] = pseudo_family.get_pseudos(structure=structure) builder.pw['structure'] = structure builder.pw['parameters'] = orm.Dict(dict=parameters) builder.pw['metadata'] = inputs['pw']['metadata'] if 'parallelization' in inputs['pw']: builder.pw['parallelization'] = orm.Dict( dict=inputs['pw']['parallelization']) builder.clean_workdir = orm.Bool(inputs['clean_workdir']) builder.kpoints_distance = orm.Float(inputs['kpoints_distance']) builder.kpoints_force_parity = orm.Bool(inputs['kpoints_force_parity']) # pylint: enable=no-member return builder
from aiida.engine import run from aiida.orm import load_code, Int from aiida.plugins import CalculationFactory ArithmeticAddCalculation = CalculationFactory('arithmetic.add') inputs = { 'code': load_code('add@localhost'), 'x': Int(1), 'y': Int(2), } run(ArithmeticAddCalculation, **inputs)
def get_builder(self, structure: StructureData, engines: Dict[str, Any], *, protocol: str = None, relax_type: RelaxType = RelaxType.POSITIONS, electronic_type: ElectronicType = ElectronicType.METAL, spin_type: SpinType = SpinType.NONE, magnetization_per_site: List[float] = None, threshold_forces: float = None, threshold_stress: float = None, reference_workchain=None, **kwargs) -> engine.ProcessBuilder: """Return a process builder for the corresponding workchain class with inputs set according to the protocol. :param structure: the structure to be relaxed. :param engines: a dictionary containing the computational resources for the relaxation. :param protocol: the protocol to use when determining the workchain inputs. :param relax_type: the type of relaxation to perform. :param electronic_type: the electronic character that is to be used for the structure. :param spin_type: the spin polarization type to use for the calculation. :param magnetization_per_site: a list with the initial spin polarization for each site. Float or integer in units of electrons. If not defined, the builder will automatically define the initial magnetization if and only if `spin_type != SpinType.NONE`. :param threshold_forces: target threshold for the forces in eV/Å. :param threshold_stress: target threshold for the stress in eV/Å^3. :param reference_workchain: a <Code>RelaxWorkChain node. :param kwargs: any inputs that are specific to the plugin. :return: a `aiida.engine.processes.ProcessBuilder` instance ready to be submitted. """ # pylint: disable=too-many-locals, too-many-branches, too-many-statements protocol = protocol or self.get_default_protocol_name() super().get_builder(structure, engines, protocol=protocol, relax_type=relax_type, electronic_type=electronic_type, spin_type=spin_type, magnetization_per_site=magnetization_per_site, threshold_forces=threshold_forces, threshold_stress=threshold_stress, reference_workchain=reference_workchain, **kwargs) # Protocol parameters = self.get_protocol(protocol) _ = parameters.pop('description') _ = parameters.pop('name') # # kpoints target_spacing = parameters.pop('kpoint_spacing') if reference_workchain: ref_kpoints = reference_workchain.inputs.nwchem__parameters[ 'nwpw']['monkhorst-pack'] parameters['nwpw']['monkhorst-pack'] = ref_kpoints else: reciprocal_axes_lengths = np.linalg.norm(np.linalg.inv( structure.cell), axis=1) kpoints = np.ceil(reciprocal_axes_lengths / target_spacing).astype(int).tolist() parameters['nwpw']['monkhorst-pack'] = '{} {} {}'.format(*kpoints) # Relaxation type if relax_type == RelaxType.POSITIONS: parameters['task'] = 'band optimize' elif relax_type == RelaxType.POSITIONS_CELL: parameters['task'] = 'band optimize' parameters['set'] = {'includestress': '.true.'} elif relax_type == RelaxType.CELL: parameters['task'] = 'band optimize' parameters['set'] = { 'includestress': '.true.', 'nwpw:zero_forces': '.true.' } elif relax_type == RelaxType.NONE: parameters['task'] = 'band gradient' _ = parameters.pop('driver') else: raise ValueError('relax_type `{}` is not supported'.format( relax_type.value)) # Electronic type if electronic_type == ElectronicType.INSULATOR: pass elif electronic_type == ElectronicType.METAL: parameters['nwpw']['smear'] = 'fermi' parameters['nwpw'][ 'scf'] = 'Anderson outer_iterations 0 Kerker 2.0' parameters['nwpw']['loop'] = '10 10' _ = parameters['nwpw'].pop('lmbfgs') # Revert to CG else: raise ValueError('electronic_type `{}` is not supported'.format( electronic_type.value)) # Spin type if spin_type == SpinType.NONE: pass elif spin_type == SpinType.COLLINEAR: parameters['nwpw']['odft'] = '' else: raise ValueError('spin_type `{}` is not supported'.format( spin_type.value)) # Magnetization per site # Not implemented yet - one has to specify the site, spin AND angular momentum if magnetization_per_site: raise ValueError('magnetization per site not yet supported') # Special case of a molecule in "open boundary conditions" if structure.pbc == (False, False, False): warnings.warn( 'PBCs set to false in input structure: assuming this is a molecular calculation' ) parameters['nwpw']['monkhorst-pack'] = '1 1 1' # Gamma only parameters['nwpw']['cutoff'] = 250 # Forces threshold. if threshold_forces is not None: parameters['driver']['xmax'] = '{}'.format(threshold_forces / HA_BOHR_TO_EV_A) # Stress threshold. if threshold_stress is not None: raise ValueError( 'Overall stress is not used as a stopping criterion in NWChem') # Prepare builder builder = self.process_class.get_builder() builder.nwchem.code = orm.load_code(engines['relax']['code']) builder.nwchem.metadata.options = engines['relax']['options'] builder.nwchem.parameters = orm.Dict(dict=parameters) builder.nwchem.add_cell = orm.Bool(True) builder.nwchem.structure = structure return builder
def get_builder(self, structure: StructureData, calc_engines: Dict[str, Any], *, protocol: str = None, relax_type: RelaxType = RelaxType.ATOMS, electronic_type: ElectronicType = ElectronicType.METAL, spin_type: SpinType = SpinType.NONE, magnetization_per_site: List[float] = None, threshold_forces: float = None, threshold_stress: float = None, previous_workchain=None, **kwargs) -> engine.ProcessBuilder: """Return a process builder for the corresponding workchain class with inputs set according to the protocol. :param structure: the structure to be relaxed. :param calc_engines: a dictionary containing the computational resources for the relaxation. :param protocol: the protocol to use when determining the workchain inputs. :param relax_type: the type of relaxation to perform. :param electronic_type: the electronic character that is to be used for the structure. :param spin_type: the spin polarization type to use for the calculation. :param magnetization_per_site: a list with the initial spin polarization for each site. Float or integer in units of electrons. If not defined, the builder will automatically define the initial magnetization if and only if `spin_type != SpinType.NONE`. :param threshold_forces: target threshold for the forces in eV/Å. :param threshold_stress: target threshold for the stress in eV/Å^3. :param previous_workchain: a <Code>RelaxWorkChain node. :param kwargs: any inputs that are specific to the plugin. :return: a `aiida.engine.processes.ProcessBuilder` instance ready to be submitted. """ # pylint: disable=too-many-locals protocol = protocol or self.get_default_protocol_name() super().get_builder(structure, calc_engines, protocol=protocol, relax_type=relax_type, electronic_type=electronic_type, spin_type=spin_type, magnetization_per_site=magnetization_per_site, threshold_forces=threshold_forces, threshold_stress=threshold_stress, previous_workchain=previous_workchain, **kwargs) # Get the protocol that we want to use if protocol is None: protocol = self._default_protocol protocol = self.get_protocol(protocol) # Set the builder builder = self.process_class.get_builder() # Set code builder.code = orm.load_code(calc_engines['relax']['code']) # Set structure builder.structure = structure # Set options builder.options = plugins.DataFactory('dict')( dict=calc_engines['relax']['options']) # Set settings # Make sure we add forces and stress for the VASP parser settings = AttributeDict() settings.update( {'parser_settings': { 'add_forces': True, 'add_stress': True }}) builder.settings = plugins.DataFactory('dict')(dict=settings) # Set workchain related inputs, in this case, give more explicit output to report builder.verbose = plugins.DataFactory('bool')(True) # Set parameters builder.parameters = plugins.DataFactory('dict')( dict=protocol['parameters']) # Set potentials and their mapping builder.potential_family = plugins.DataFactory('str')( protocol['potential_family']) builder.potential_mapping = plugins.DataFactory('dict')( dict=self._potential_mapping[protocol['potential_mapping']]) # Set the kpoint grid from the density in the protocol kpoints = plugins.DataFactory('array.kpoints')() kpoints.set_kpoints_mesh([1, 1, 1]) kpoints.set_cell_from_structure(structure) rec_cell = kpoints.reciprocal_cell kpoints.set_kpoints_mesh( fetch_k_grid(rec_cell, protocol['kpoint_distance'])) builder.kpoints = kpoints # Here we set the protocols fast, moderate and precise. These currently have no formal meaning. # After a while these will be set in the VASP workchain entrypoints using the convergence workchain etc. # However, for now we rely on defaults plane wave cutoffs and a set k-point density for the chosen protocol. relax = AttributeDict() relax.perform = plugins.DataFactory('bool')(True) relax.algo = plugins.DataFactory('str')(protocol['relax']['algo']) if relax_type == RelaxType.ATOMS: relax.positions = plugins.DataFactory('bool')(True) relax.shape = plugins.DataFactory('bool')(False) relax.volume = plugins.DataFactory('bool')(False) elif relax_type == RelaxType.CELL: relax.positions = plugins.DataFactory('bool')(False) relax.shape = plugins.DataFactory('bool')(True) relax.volume = plugins.DataFactory('bool')(True) elif relax_type == RelaxType.ATOMS_CELL: relax.positions = plugins.DataFactory('bool')(True) relax.shape = plugins.DataFactory('bool')(True) relax.volume = plugins.DataFactory('bool')(True) else: raise ValueError('relaxation type `{}` is not supported'.format( relax_type.value)) if threshold_forces is not None: threshold = threshold_forces else: threshold = protocol['relax']['threshold_forces'] relax.force_cutoff = plugins.DataFactory('float')(threshold) if threshold_stress is not None: raise ValueError( 'Using a stress threshold is not directly available in VASP during relaxation.' ) builder.relax = relax return builder
def get_builder(self, structure: StructureData, calc_engines: Dict[str, Any], *, protocol: str = None, relax_type: RelaxType = RelaxType.ATOMS, electronic_type: ElectronicType = ElectronicType.METAL, spin_type: SpinType = SpinType.NONE, magnetization_per_site: List[float] = None, threshold_forces: float = None, threshold_stress: float = None, previous_workchain=None, **kwargs) -> engine.ProcessBuilder: """Return a process builder for the corresponding workchain class with inputs set according to the protocol. :param structure: the structure to be relaxed. :param calc_engines: a dictionary containing the computational resources for the relaxation. :param protocol: the protocol to use when determining the workchain inputs. :param relax_type: the type of relaxation to perform. :param electronic_type: the electronic character that is to be used for the structure. :param spin_type: the spin polarization type to use for the calculation. :param magnetization_per_site: a list with the initial spin polarization for each site. Float or integer in units of electrons. If not defined, the builder will automatically define the initial magnetization if and only if `spin_type != SpinType.NONE`. :param threshold_forces: target threshold for the forces in eV/Å. :param threshold_stress: target threshold for the stress in eV/Å^3. :param previous_workchain: a <Code>RelaxWorkChain node. :param kwargs: any inputs that are specific to the plugin. :return: a `aiida.engine.processes.ProcessBuilder` instance ready to be submitted. """ # pylint: disable=too-many-locals, too-many-branches, too-many-statements protocol = protocol or self.get_default_protocol_name() super().get_builder(structure, calc_engines, protocol=protocol, relax_type=relax_type, electronic_type=electronic_type, spin_type=spin_type, magnetization_per_site=magnetization_per_site, threshold_forces=threshold_forces, threshold_stress=threshold_stress, previous_workchain=previous_workchain, **kwargs) builder = self.process_class.get_builder() if relax_type == RelaxType.ATOMS: relaxation_schema = 'relax' elif relax_type == RelaxType.NONE: relaxation_schema = 'relax' builder.relax.perform = orm.Bool(False) else: raise ValueError('relaxation type `{}` is not supported'.format( relax_type.value)) pymatgen_struct = structure.get_pymatgen() ortho_dict = None if pymatgen_struct.ntypesp <= 1: # pass the structure through a transform to generate orthorhombic structure if possible/needed. new = ortho_struct(structure) newstruct = new.get('outstruct') ortho_dict = new.get('outdict') newstruct.store() builder.structure = newstruct else: builder.structure = structure # for now apply simple stupid heuristic : atoms < 200 -> cubic, else -> linear. import copy if len(builder.structure.sites) <= 200: inputdict = copy.deepcopy( self.get_protocol(protocol)['inputdict_cubic']) else: inputdict = copy.deepcopy( self.get_protocol(protocol)['inputdict_linear']) # adapt hgrid to the strain if previous_workchain is not None and previous_workchain.is_finished_ok: logfile = previous_workchain.outputs.bigdft_logfile.logfile if isinstance(logfile, list): hgrids = logfile[0].get('dft').get('hgrids') else: hgrids = logfile.get('dft').get('hgrids') inputdict['dft']['hgrids'] = hgrids[0] * builder.structure.cell_lengths[0] / \ previous_workchain.inputs.structure.cell_lengths[0] # Soon : Use inputActions if electronic_type is ElectronicType.METAL: if 'mix' not in inputdict: inputdict['mix'] = {} inputdict['mix'].update({ 'iscf': 17, 'itrpmax': 200, 'rpnrm_cv': 1.E-12, 'norbsempty': 120, 'tel': 0.01, 'alphamix': 0.8, 'alphadiis': 1.0 }) if spin_type is SpinType.NONE: inputdict['dft'].update({'nspin': 1}) elif spin_type is SpinType.COLLINEAR: inputdict['dft'].update({'nspin': 2}) psp = [] if ortho_dict is not None: inputdict = BigDFTParameters.set_inputfile( inputdict['dft']['hgrids'], ortho_dict, inputdict, psp=psp, units='angstroem') else: # use HGH pseudopotentials instead of default ones from BigDFT, if the user does not specify new ones. # This may be moved to the plugin if we decide to make it the default behavior. for elem in pymatgen_struct.types_of_specie: BigDFTParameters.set_psp(elem.name, psp) inputdict['kpt'] = BigDFTParameters.set_kpoints( len(builder.structure.sites)) if pymatgen_struct.ntypesp <= 1: inputdict['dft'].update( BigDFTParameters.set_spin( builder.structure.sites[0].kind_name, len(builder.structure.sites))) if magnetization_per_site: for (i, atom) in enumerate(inputdict['posinp']['positions']): atom['IGSpin'] = int(magnetization_per_site[i]) if psp: import os builder.pseudos = orm.List() psprel = [os.path.relpath(i) for i in psp] builder.pseudos.extend(psprel) builder.parameters = BigDFTParameters(dict=inputdict) builder.code = orm.load_code(calc_engines[relaxation_schema]['code']) run_opts = {'options': calc_engines[relaxation_schema]['options']} builder.run_opts = orm.Dict(dict=run_opts) if threshold_forces is not None: builder.relax.threshold_forces = orm.Float(threshold_forces) return builder
def _construct_builder(self, **kwargs) -> engine.ProcessBuilder: """Construct a process builder based on the provided keyword arguments. The keyword arguments will have been validated against the input generator specification. """ # pylint: disable=too-many-branches,too-many-statements,too-many-locals structure = kwargs['structure'] engines = kwargs['engines'] protocol = kwargs['protocol'] spin_type = kwargs['spin_type'] relax_type = kwargs['relax_type'] magnetization_per_site = kwargs.get('magnetization_per_site', None) threshold_forces = kwargs.get('threshold_forces', None) if any(structure.get_attribute_many(['pbc1', 'pbc2', 'pbc3'])): print('Warning: PBC detected in input structure. It is not supported and thus ignored.') # ----------------------------------------------------------------- # Set the link0 memory and n_proc based on the engines options dict link0_parameters = {'%chk': 'aiida.chk'} options = engines['relax']['options'] res = options['resources'] if 'max_memory_kb' not in options: # If memory is not set, set a default of 2 GB link0_parameters['%mem'] = '2048MB' else: # If memory is set, specify 80% of it to gaussian link0_parameters['%mem'] = '%dMB' % ((0.8 * options['max_memory_kb']) // 1024) # Determine the number of processors that should be specified to Gaussian n_proc = None if 'tot_num_mpiprocs' in res: n_proc = res['tot_num_mpiprocs'] elif 'num_machines' in res: if 'num_mpiprocs_per_machine' in res: n_proc = res['num_machines'] * res['num_mpiprocs_per_machine'] else: code = load_code(engines['relax']['code']) def_mppm = code.computer.get_default_mpiprocs_per_machine() if def_mppm is not None: n_proc = res['num_machines'] * def_mppm if n_proc is not None: link0_parameters['%nprocshared'] = '%d' % n_proc # ----------------------------------------------------------------- # General route parameters sel_protocol = copy.deepcopy(self.get_protocol(protocol)) route_params = sel_protocol['route_parameters'] if relax_type == RelaxType.NONE: del route_params['opt'] route_params['force'] = None if threshold_forces is not None: # Set the RMS force threshold with the iop(1/7=N) command # threshold = N * 10**(-6) in [EH/Bohr] threshold_forces_au = threshold_forces * EV_TO_EH / ANG_TO_BOHR if threshold_forces_au < 1e-6: print('Warning: Forces threshold cannot be lower than 1e-6 au.') threshold_forces_au = 1e-6 threshold_forces_n = int(np.round(threshold_forces_au * 1e6)) route_params['iop(1/7=%d)' % threshold_forces_n] = None # ----------------------------------------------------------------- # Handle spin-polarization pymatgen_structure = structure.get_pymatgen_molecule() num_electrons = pymatgen_structure.nelectrons spin_multiplicity = 1 if num_electrons % 2 == 1 and spin_type == SpinType.NONE: raise ValueError(f'Spin-restricted calculation does not support odd number of electrons ({num_electrons})') if spin_type == SpinType.COLLINEAR: # enable UKS sel_protocol['functional'] = 'U' + sel_protocol['functional'] # determine the spin multiplicity if magnetization_per_site is None: multiplicity_guess = 1 else: print( 'Warning: magnetization_per_site site-resolved info is disregarded, only total spin is processed.' ) # magnetization_per_site are in units of [Bohr magnetons] (*0.5 to get in [au]) total_spin_guess = 0.5 * np.abs(np.sum(magnetization_per_site)) multiplicity_guess = 2 * total_spin_guess + 1 # in case of even/odd electrons, find closest odd/even multiplicity if num_electrons % 2 == 0: # round guess to nearest odd integer spin_multiplicity = int(np.round((multiplicity_guess - 1) / 2) * 2 + 1) else: # round guess to nearest even integer; 0 goes to 2 spin_multiplicity = max([int(np.round(multiplicity_guess / 2) * 2), 2]) # Mix H**O and LUMO if we're looking for the open-shell singlet if spin_multiplicity == 1: route_params['guess'] = 'mix' # ----------------------------------------------------------------- # Build the builder params = { 'link0_parameters': link0_parameters, 'functional': sel_protocol['functional'], 'basis_set': sel_protocol['basis_set'], 'charge': 0, 'multiplicity': spin_multiplicity, 'route_parameters': route_params } builder = self.process_class.get_builder() builder.gaussian.structure = structure builder.gaussian.parameters = orm.Dict(dict=params) builder.gaussian.code = orm.load_code(engines['relax']['code']) builder.gaussian.metadata.options = engines['relax']['options'] return builder
StructureData = DataFactory('structure') try: codename = sys.argv[1] except IndexError: codename = 'SiestaHere@localhost' try: stm_codename = sys.argv[2] except IndexError: stm_codename = 'STMhere@localhost' # #------------------Code and computer options --------------------------- # code = load_code(codename) stm_code = load_code(stm_codename) options = { "max_wallclock_seconds": 33360, 'withmpi': True, #'account': "tcphy113c", #'queue_name': "DevQ", "resources": { "num_machines": 1, "num_mpiprocs_per_machine": 4, } } # # Structure, a Cr slab #
def main(): """Launch a bunch of calculation jobs and workchains.""" # pylint: disable=too-many-locals,too-many-statements,too-many-branches expected_results_process_functions = {} expected_results_calculations = {} expected_results_workchains = {} code_doubler = load_code(CODENAME_DOUBLER) # Run the `ArithmeticAddCalculation` print('Running the `ArithmeticAddCalculation`') run_arithmetic_add() # Run the `AddArithmeticBaseWorkChain` print('Running the `AddArithmeticBaseWorkChain`') run_base_restart_workchain() # Run the `MultiplyAddWorkChain` print('Running the `MultiplyAddWorkChain`') run_multiply_add_workchain() # Submitting the calcfunction through the launchers print('Submitting calcfunction to the daemon') proc, expected_result = launch_calcfunction(inputval=1) expected_results_process_functions[proc.pk] = expected_result # Submitting the workfunction through the launchers print('Submitting workfunction to the daemon') proc, expected_result = launch_workfunction(inputval=1) expected_results_process_functions[proc.pk] = expected_result # Submitting the Calculations the new way directly through the launchers print(f'Submitting {NUMBER_CALCULATIONS} calculations to the daemon') for counter in range(1, NUMBER_CALCULATIONS + 1): inputval = counter calc, expected_result = launch_calculation(code=code_doubler, counter=counter, inputval=inputval) expected_results_calculations[calc.pk] = expected_result # Submitting the Workchains print(f'Submitting {NUMBER_WORKCHAINS} workchains to the daemon') for index in range(NUMBER_WORKCHAINS): inp = Int(index) _, node = run.get_node(NestedWorkChain, inp=inp) expected_results_workchains[node.pk] = index print("Submitting a workchain with 'submit'.") builder = NestedWorkChain.get_builder() input_val = 4 builder.inp = Int(input_val) proc = submit(builder) expected_results_workchains[proc.pk] = input_val print('Submitting a workchain with a nested input namespace.') value = Int(-12) pk = submit(NestedInputNamespace, foo={'bar': {'baz': value}}).pk print('Submitting a workchain with a dynamic non-db input.') value = [4, 2, 3] pk = submit(DynamicNonDbInput, namespace={'input': value}).pk expected_results_workchains[pk] = value print('Submitting a workchain with a dynamic db input.') value = 9 pk = submit(DynamicDbInput, namespace={'input': Int(value)}).pk expected_results_workchains[pk] = value print('Submitting a workchain with a mixed (db / non-db) dynamic input.') value_non_db = 3 value_db = Int(2) pk = submit(DynamicMixedInput, namespace={ 'inputs': { 'input_non_db': value_non_db, 'input_db': value_db } }).pk expected_results_workchains[pk] = value_non_db + value_db print('Submitting the serializing workchain') pk = submit(SerializeWorkChain, test=Int).pk expected_results_workchains[pk] = ObjectLoader().identify_object(Int) print('Submitting the ListEcho workchain.') list_value = List() list_value.extend([1, 2, 3]) pk = submit(ListEcho, list=list_value).pk expected_results_workchains[pk] = list_value print('Submitting a WorkChain which contains a workfunction.') value = Str('workfunction test string') pk = submit(WorkFunctionRunnerWorkChain, input=value).pk expected_results_workchains[pk] = value print('Submitting a WorkChain which contains a calcfunction.') value = Int(1) pk = submit(CalcFunctionRunnerWorkChain, input=value).pk expected_results_workchains[pk] = Int(2) calculation_pks = sorted(expected_results_calculations.keys()) workchains_pks = sorted(expected_results_workchains.keys()) process_functions_pks = sorted(expected_results_process_functions.keys()) pks = calculation_pks + workchains_pks + process_functions_pks print('Wating for end of execution...') start_time = time.time() exited_with_timeout = True while time.time() - start_time < TIMEOUTSECS: time.sleep(15) # Wait a few seconds # Print some debug info, both for debugging reasons and to avoid # that the test machine is shut down because there is no output print('#' * 78) print(f'####### TIME ELAPSED: {time.time() - start_time} s') print('#' * 78) print("Output of 'verdi process list -a':") try: print( subprocess.check_output( ['verdi', 'process', 'list', '-a'], stderr=subprocess.STDOUT, )) except subprocess.CalledProcessError as exception: print(f'Note: the command failed, message: {exception}') print("Output of 'verdi daemon status':") try: print( subprocess.check_output( ['verdi', 'daemon', 'status'], stderr=subprocess.STDOUT, )) except subprocess.CalledProcessError as exception: print(f'Note: the command failed, message: {exception}') if jobs_have_finished(pks): print('Calculation terminated its execution') exited_with_timeout = False break if exited_with_timeout: print_daemon_log() print('') print( f'Timeout!! Calculation did not complete after {TIMEOUTSECS} seconds' ) sys.exit(2) else: # Launch the same calculations but with caching enabled -- these should be FINISHED immediately cached_calcs = [] with enable_caching(identifier='aiida.calculations:templatereplacer'): for counter in range(1, NUMBER_CALCULATIONS + 1): inputval = counter calc, expected_result = run_calculation(code=code_doubler, counter=counter, inputval=inputval) cached_calcs.append(calc) expected_results_calculations[calc.pk] = expected_result if (validate_calculations(expected_results_calculations) and validate_workchains(expected_results_workchains) and validate_cached(cached_calcs) and validate_process_functions( expected_results_process_functions)): print_daemon_log() print('') print('OK, all calculations have the expected parsed result') sys.exit(0) else: print_daemon_log() print('') print( 'ERROR! Some return values are different from the expected value' ) sys.exit(3)
def get_builder(self, structure: StructureData, calc_engines: Dict[str, Any], *, protocol: str = None, relax_type: RelaxType = RelaxType.ATOMS, electronic_type: ElectronicType = ElectronicType.METAL, spin_type: SpinType = SpinType.NONE, magnetization_per_site: List[float] = None, threshold_forces: float = None, threshold_stress: float = None, previous_workchain=None, **kwargs) -> engine.ProcessBuilder: """Return a process builder for the corresponding workchain class with inputs set according to the protocol. :param structure: the structure to be relaxed. :param calc_engines: a dictionary containing the computational resources for the relaxation. :param protocol: the protocol to use when determining the workchain inputs. :param relax_type: the type of relaxation to perform. :param electronic_type: the electronic character that is to be used for the structure. :param spin_type: the spin polarization type to use for the calculation. :param magnetization_per_site: a list with the initial spin polarization for each site. Float or integer in units of electrons. If not defined, the builder will automatically define the initial magnetization if and only if `spin_type != SpinType.NONE`. :param threshold_forces: target threshold for the forces in eV/Å. :param threshold_stress: target threshold for the stress in eV/Å^3. :param previous_workchain: a <Code>RelaxWorkChain node. :param kwargs: any inputs that are specific to the plugin. :return: a `aiida.engine.processes.ProcessBuilder` instance ready to be submitted. """ # pylint: disable=too-many-locals protocol = protocol or self.get_default_protocol_name() super().get_builder(structure, calc_engines, protocol=protocol, relax_type=relax_type, electronic_type=electronic_type, spin_type=spin_type, magnetization_per_site=magnetization_per_site, threshold_forces=threshold_forces, threshold_stress=threshold_stress, previous_workchain=previous_workchain, **kwargs) # pylint: disable=too-many-locals inpgen_code = calc_engines['inpgen']['code'] fleur_code = calc_engines['relax']['code'] if not isinstance(inpgen_code, orm.Code): inpgen_code = orm.load_code(inpgen_code) if not isinstance(fleur_code, orm.Code): fleur_code = orm.load_code(fleur_code) # Checks if protocol exists if protocol not in self.get_protocol_names(): import warnings warnings.warn( 'no protocol implemented with name {}, using default moderate'. format(protocol)) protocol = self.get_default_protocol_name() else: protocol = self.get_protocol(protocol) builder = self.process_class.get_builder() # implement this, protocol dependent, we still have option keys as nodes ... # has to go over calc parameters, kmax, lmax, kpoint density # inputs = generate_scf_inputs(process_class, protocol, code, structure) if relax_type == RelaxType.ATOMS: relaxation_mode = 'force' else: raise ValueError('relaxation type `{}` is not supported'.format( relax_type.value)) if threshold_forces is not None: # Fleur expects atomic units i.e Hartree/bohr conversion_fac = 51.421 force_criterion = threshold_forces / conversion_fac else: force_criterion = 0.001 if threshold_stress is not None: pass # Stress is not supported film_relax = not structure.pbc[-1] default_wf_para = { 'relax_iter': 5, 'film_distance_relaxation': film_relax, 'force_criterion': force_criterion, 'change_mixing_criterion': 0.025, 'atoms_off': [], 'run_final_scf': True # we always run a final scf after the relaxation } wf_para_dict = recursive_merge(default_wf_para, protocol.get('relax', {})) wf_para = orm.Dict(dict=wf_para_dict) default_scf = { 'fleur_runmax': 2, 'itmax_per_run': 120, 'force_converged': force_criterion, 'force_dict': { 'qfix': 2, 'forcealpha': 0.75, 'forcemix': 'straight' }, 'use_relax_xml': True, 'serial': False, 'mode': relaxation_mode, } wf_para_scf_dict = recursive_merge(default_scf, protocol.get('scf', {})) wf_para_scf = orm.Dict(dict=wf_para_scf_dict) inputs = { 'scf': { 'wf_parameters': wf_para_scf, 'structure': structure, # 'calc_parameters': parameters, # protocol depended # 'options': options_scf, # options do not matter on QM, in general they do... 'inpgen': inpgen_code, 'fleur': fleur_code }, 'wf_parameters': wf_para } if previous_workchain is not None: parameters = get_parameters(previous_workchain) inputs['scf']['calc_parameters'] = parameters # User specification overrides previous workchain! if 'calc_parameters' in kwargs.keys(): parameters = kwargs.pop('calc_parameters') inputs['scf']['calc_parameters'] = parameters builder._update(inputs) # pylint: disable=protected-access return builder
else: raise IndexError except IndexError: print >> sys.stderr, ("The first parameter can only be either " "--send or --dont-send") sys.exit(1) try: codename = sys.argv[2] except IndexError: codename = 'Siesta4.0.1@kelvin' # #------------------Code and computer options --------------------------- # code = load_code(codename) options = { # "queue_name": "debug", "max_wallclock_seconds": 1700, 'withmpi': True, "resources": { "num_machines": 1, "num_mpiprocs_per_machine": 2, } } # # Structure ----------------------------------------- # # BCC
def get_builder(self, structure: StructureData, calc_engines: Dict[str, Any], *, protocol: str = None, relax_type: RelaxType = RelaxType.ATOMS, electronic_type: ElectronicType = ElectronicType.METAL, spin_type: SpinType = SpinType.NONE, magnetization_per_site: List[float] = None, threshold_forces: float = None, threshold_stress: float = None, previous_workchain=None, **kwargs) -> engine.ProcessBuilder: """Return a process builder for the corresponding workchain class with inputs set according to the protocol. :param structure: the structure to be relaxed. :param calc_engines: a dictionary containing the computational resources for the relaxation. :param protocol: the protocol to use when determining the workchain inputs. :param relax_type: the type of relaxation to perform. :param electronic_type: the electronic character that is to be used for the structure. :param spin_type: the spin polarization type to use for the calculation. :param magnetization_per_site: a list with the initial spin polarization for each site. Float or integer in units of electrons. If not defined, the builder will automatically define the initial magnetization if and only if `spin_type != SpinType.NONE`. :param threshold_forces: target threshold for the forces in eV/Å. :param threshold_stress: target threshold for the stress in eV/Å^3. :param previous_workchain: a <Code>RelaxWorkChain node. :param kwargs: any inputs that are specific to the plugin. :return: a `aiida.engine.processes.ProcessBuilder` instance ready to be submitted. """ # pylint: disable=too-many-locals,too-many-branches protocol = protocol or self.get_default_protocol_name() super().get_builder(structure, calc_engines, protocol=protocol, relax_type=relax_type, electronic_type=electronic_type, spin_type=spin_type, magnetization_per_site=magnetization_per_site, threshold_forces=threshold_forces, threshold_stress=threshold_stress, previous_workchain=previous_workchain, **kwargs) if magnetization_per_site is not None: print( 'Warning: magnetization_per_site not supported, ignoring it.') # ----------------------------------------------------------------- # Set the link0 memory and n_proc based on the calc_engines options dict link0_parameters = {'%chk': 'aiida.chk'} options = calc_engines['relax']['options'] res = options['resources'] if 'max_memory_kb' not in options: # If memory is not set, set a default of 2 GB link0_parameters['%mem'] = '2048MB' else: # If memory is set, specify 80% of it to gaussian link0_parameters['%mem'] = '%dMB' % ( (0.8 * options['max_memory_kb']) // 1024) # Determine the number of processors that should be specified to Gaussian n_proc = None if 'tot_num_mpiprocs' in res: n_proc = res['tot_num_mpiprocs'] elif 'num_machines' in res: if 'num_mpiprocs_per_machine' in res: n_proc = res['num_machines'] * res['num_mpiprocs_per_machine'] else: code = load_code(calc_engines['relax']['code']) def_mppm = code.computer.get_default_mpiprocs_per_machine() if def_mppm is not None: n_proc = res['num_machines'] * def_mppm if n_proc is not None: link0_parameters['%nprocshared'] = '%d' % n_proc # ----------------------------------------------------------------- sel_protocol = copy.deepcopy(self.get_protocol(protocol)) route_params = sel_protocol['route_parameters'] if relax_type == RelaxType.NONE: del route_params['opt'] route_params['force'] = None if spin_type == SpinType.COLLINEAR: # In case of collinear spin, enable UKS and specify guess=mix sel_protocol['functional'] = 'U' + sel_protocol['functional'] route_params['guess'] = 'mix' if threshold_forces is not None: # Set the RMS force threshold with the iop(1/7=N) command # threshold = N * 10**(-6) in [EH/Bohr] threshold_forces_au = threshold_forces * EV_TO_EH / ANG_TO_BOHR if threshold_forces_au < 1e-6: print( 'Warning: Forces threshold cannot be lower than 1e-6 au.') threshold_forces_au = 1e-6 threshold_forces_n = int(np.round(threshold_forces_au * 1e6)) route_params['iop(1/7=%d)' % threshold_forces_n] = None params = { 'link0_parameters': link0_parameters, 'functional': sel_protocol['functional'], 'basis_set': sel_protocol['basis_set'], 'charge': 0, 'multiplicity': 1, 'route_parameters': route_params } builder = self.process_class.get_builder() builder.gaussian.structure = structure builder.gaussian.parameters = orm.Dict(dict=params) builder.gaussian.code = orm.load_code(calc_engines['relax']['code']) builder.gaussian.metadata.options = calc_engines['relax']['options'] return builder
def generate_inputs(process_class: engine.Process, protocol: Dict, code: orm.Code, structure: orm.StructureData, override: Dict[str, Any] = None) -> Dict[str, Any]: """Generate the input parameters for the given workchain type for a given code, structure and pseudo family. The override argument can be used to pass a dictionary with values for specific inputs that should override the defaults. This dictionary should have the same nested structure as the final input dictionary would have for the workchain submission. For example if one wanted to generate the inputs for a PwBandsWorkChain and override the ecutwfc parameter for the PwBaseWorkChain of the PwRelaxWorkChains, one would have to pass: override = {'relax': {'base': {'ecutwfc': 400}}} :param process_class: process class, either calculation or workchain, i.e. ``PwCalculation`` or ``PwBaseWorkChain`` :param protocol: the protocol based on which to choose input parameters :param code: the code or code name to use :param structure: the structure :param override: a dictionary to override specific inputs :return: input dictionary """ # pylint: disable=too-many-arguments,unused-argument from aiida.common.lang import type_check family_name = protocol['relax']['base']['pseudos_family'] if isinstance(family_name, orm.Str): family_name = family_name.value try: otfg_family = OTFGGroup.objects.get(label=family_name) except exceptions.NotExistent: raise ValueError( 'protocol `{}` requires the `{}` `pseudos family` but could not be found.' .format(protocol['name'], protocol['relax']['base']['pseudos_family'])) CastepCalculation = plugins.CalculationFactory('castep.castep') # pylint: disable=invalid-name CastepBaseWorkChain = plugins.WorkflowFactory('castep.base') # pylint: disable=invalid-name CastepRelaxWorkChain = plugins.WorkflowFactory('castep.relax') # pylint: disable=invalid-name type_check(structure, orm.StructureData) if not isinstance(code, orm.Code): try: code = orm.load_code(code) except (exceptions.MultipleObjectsError, exceptions.NotExistent) as exception: raise ValueError('could not load the code {}: {}'.format( code, exception)) if process_class == CastepCalculation: protocol = protocol['relax']['base']['calc'] dictionary = generate_inputs_calculation(protocol, code, structure, otfg_family, override) elif process_class == CastepBaseWorkChain: protocol = protocol['relax']['base'] dictionary = generate_inputs_base(protocol, code, structure, otfg_family, override) elif process_class == CastepRelaxWorkChain: protocol = protocol['relax'] dictionary = generate_inputs_relax(protocol, code, structure, otfg_family, override) else: raise NotImplementedError( 'process class {} is not supported'.format(process_class)) return dictionary
from aiida.engine import run from aiida.orm import Str, Dict, KpointsData, StructureData, load_code from aiida.plugins import WorkflowFactory from aiida_wannier90.orbitals import generate_projections pw_code = load_code("<CODE LABEL>") # Replace with the QE pw.x code label wannier_code = load_code( "<CODE LABEL>") # Replace with the Wannier90 wannier.x code label pw2wannier90_code = load_code( "<CODE LABEL>") # Replace with the QE pw2wannier90.x code label pseudo_family_name = "<UPF FAMILY NAME>" # Replace with the name of the pseudopotential family for SSSP efficiency # GaAs structure a = 5.68018817933178 # angstrom structure = StructureData( cell=[[-a / 2., 0, a / 2.], [0, a / 2., a / 2.], [-a / 2., a / 2., 0]]) structure.append_atom(symbols=['Ga'], position=(0., 0., 0.)) structure.append_atom(symbols=['As'], position=(-a / 4., a / 4., a / 4.)) # 4x4x4 k-points mesh for the SCF kpoints_scf = KpointsData() kpoints_scf.set_kpoints_mesh([4, 4, 4]) # 10x10x10 k-points mesh for the NSCF/Wannier90 calculations kpoints_nscf = KpointsData() kpoints_nscf.set_kpoints_mesh([10, 10, 10]) # k-points path for the band structure kpoint_path = Dict( dict={