Beispiel #1
0
 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'])))
Beispiel #2
0
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')
Beispiel #3
0
 def _load_code(code):
     if code is not None:
         try:
             return load_code(code)
         except NotExistent as error:
             print("error", error)
             return None
Beispiel #4
0
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
Beispiel #5
0
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)
Beispiel #7
0
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
Beispiel #9
0
    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
Beispiel #10
0
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
Beispiel #11
0
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
Beispiel #13
0
    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
Beispiel #18
0
    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
Beispiel #19
0
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
Beispiel #21
0
    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
#
Beispiel #25
0
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={