def cmd_dissociation_curve(plugin, structure, codes, protocol, spin_type, number_machines, wallclock_seconds, daemon, magnetization_per_site, show_engines): """Compute the dissociation curve of a diatomic molecule using the common relax workflow. The relaxation type is constrained to be `RelaxType.NONE`, meaning a single point calculation. It does not make sense to have any other type of relaxation for this task. The codes required for the plugin workflow in order to complete the task can be passed with the option `-X`, however, if no code is passed, the command will automatically try to find and load the codes that are required. If no code is installed for at least one of the calculation engines, the command will fail. Use the `--show-engine` flag to display the required calculation engines for the selected plugin workflow. """ # pylint: disable=too-many-locals from aiida_common_workflows.plugins import get_entry_point_name_from_class from aiida_common_workflows.workflows.dissociation import DissociationCurveWorkChain from aiida_common_workflows.workflows.relax.generator import RelaxType process_class = load_workflow_entry_point('relax', plugin) generator = process_class.get_inputs_generator() number_engines = len(generator.get_calc_types()) if number_machines is None: number_machines = [1] * number_engines if len(number_machines) != number_engines: raise click.BadParameter( f'{process_class.__name__} has {number_engines} engine steps, so requires {number_engines} values', param_hint='--number-machines') if wallclock_seconds is None: wallclock_seconds = [1 * 3600] * number_engines if len(wallclock_seconds) != number_engines: raise click.BadParameter( f'{process_class.__name__} has {number_engines} engine steps, so requires {number_engines} values', param_hint='--wallclock-seconds') if not generator.is_valid_protocol(protocol): protocols = generator.get_protocol_names() process_class_name = process_class.__name__ message = f'`{protocol}` is not implemented by `{process_class_name}` workflow: choose one of {protocols}' raise click.BadParameter(message, param_hint='protocol') if show_engines: for engine in generator.get_calc_types(): schema = generator.get_calc_type_schema(engine) click.secho(engine, fg='red', bold=True) click.echo('Required code plugin: {}'.format( schema['code_plugin'])) click.echo('Engine description: {}'.format( schema['description'])) return engines = {} for index, engine in enumerate(generator.get_calc_types()): schema = generator.get_calc_type_schema(engine) code_plugin = schema['code_plugin'] code = utils.get_code_from_list_or_database(codes or [], code_plugin) if code is None: raise click.UsageError( f'could not find a configured code for the plugin `{code_plugin}`. ' 'Either provide it with the -X option or make sure such a code is configured in the DB.' ) engines[engine] = { 'code': code.full_label, 'options': { 'resources': { 'num_machines': number_machines[index] }, 'max_wallclock_seconds': wallclock_seconds[index], } } inputs = { 'molecule': structure, 'generator_inputs': { 'calc_engines': engines, 'protocol': protocol, 'relax_type': RelaxType.NONE, 'spin_type': spin_type, }, 'sub_process_class': get_entry_point_name_from_class(process_class).name, } if magnetization_per_site is not None: inputs['generator_inputs'][ 'magnetization_per_site'] = magnetization_per_site utils.launch_process(DissociationCurveWorkChain, daemon, **inputs)
def cmd_eos( plugin, structure, protocol, threshold_forces, threshold_stress, number_machines, wallclock_seconds, daemon, show_engines ): """Compute the equation of state of a crystal structure using the common relax workflow. The command will automatically try to find and load the codes that are required by the plugin workflow. If no code is installed for at least one of the calculation engines, the command will fail. Use the `--show-engine` flag to display the required calculation engines for the selected plugin workflow. """ # pylint: disable=too-many-locals from aiida.orm import QueryBuilder, Code from aiida_common_workflows.plugins import get_entry_point_name_from_class from aiida_common_workflows.workflows.eos import EquationOfStateWorkChain from aiida_common_workflows.workflows.relax import RelaxType process_class = load_workflow_entry_point('relax', plugin) generator = process_class.get_inputs_generator() number_engines = len(generator.get_calc_types()) if number_machines is None: number_machines = [1] * number_engines if len(number_machines) != number_engines: raise click.BadParameter( f'{process_class.__name__} has {number_engines} engine steps, so requires {number_engines} values', param_hint='--number-machines' ) if wallclock_seconds is None: wallclock_seconds = [1 * 3600] * number_engines if len(wallclock_seconds) != number_engines: raise click.BadParameter( f'{process_class.__name__} has {number_engines} engine steps, so requires {number_engines} values', param_hint='--wallclock-seconds' ) if not generator.is_valid_protocol(protocol): protocols = generator.get_protocol_names() process_class_name = process_class.__name__ message = f'`{protocol}` is not implemented by `{process_class_name}` workflow: choose one of {protocols}' raise click.BadParameter(message, param_hint='protocol') if show_engines: for engine in generator.get_calc_types(): schema = generator.get_calc_type_schema(engine) click.secho(engine, fg='red', bold=True) click.echo('Required code plugin: {}'.format(schema['code_plugin'])) click.echo('Engine description: {}'.format(schema['description'])) return engines = {} for index, engine in enumerate(generator.get_calc_types()): schema = generator.get_calc_type_schema(engine) engines[engine] = { 'options': { 'resources': { 'num_machines': number_machines[index] }, 'max_wallclock_seconds': wallclock_seconds[index], } } code_plugin = schema['code_plugin'] query = QueryBuilder().append(Code, filters={'attributes.input_plugin': code_plugin}) code = query.first() if code is None: raise click.UsageError(f'could not find a configured code for the plugin `{code_plugin}`.') engines[engine]['code'] = code[0].full_label inputs = { 'structure': structure, 'generator_inputs': { 'calc_engines': engines, 'protocol': protocol, 'relaxation_type': RelaxType.ATOMS, }, 'sub_process_class': get_entry_point_name_from_class(process_class).name, } if threshold_forces is not None: inputs['generator_inputs']['threshold_forces'] = threshold_forces if threshold_stress is not None: inputs['generator_inputs']['threshold_stress'] = threshold_stress utils.launch_process(EquationOfStateWorkChain, daemon, **inputs)
def test_validate_sub_process_class_plugins(ctx, common_relax_workchain): """Test the `validate_sub_process_class` validator.""" from aiida_common_workflows.plugins import get_entry_point_name_from_class assert dissociation.validate_sub_process_class( get_entry_point_name_from_class(common_relax_workchain).name, ctx) is None
def cmd_eos( # pylint: disable=too-many-branches,too-many-statements plugin, structure, codes, protocol, relax_type, electronic_type, spin_type, threshold_forces, threshold_stress, number_machines, number_mpi_procs_per_machine, number_cores_per_mpiproc, wallclock_seconds, daemon, magnetization_per_site, engine_options, show_engines): """Compute the equation of state of a crystal structure using the common relax workflow. The codes required for the plugin workflow in order to complete the task can be passed with the option `-X`, however, if no code is passed, the command will automatically try to find and load the codes that are required. If no code is installed for at least one of the calculation engines, the command will fail. Use the `--show-engine` flag to display the required calculation engines for the selected plugin workflow. """ # pylint: disable=too-many-locals from aiida_common_workflows.plugins import get_entry_point_name_from_class from aiida_common_workflows.workflows.eos import EquationOfStateWorkChain process_class = load_workflow_entry_point('relax', plugin) generator = process_class.get_input_generator() number_engines = len(generator.spec().inputs['engines']) if number_machines is None: number_machines = [1] * number_engines if len(number_machines) != number_engines: raise click.BadParameter( f'{process_class.__name__} has {number_engines} engine steps, so requires {number_engines} values', param_hint='--number-machines') if number_mpi_procs_per_machine is not None and len( number_mpi_procs_per_machine) != number_engines: raise click.BadParameter( f'{process_class.__name__} has {number_engines} engine steps, so requires {number_engines} values', param_hint='--number-mpi-procs-per-machine') if number_cores_per_mpiproc is not None and len( number_cores_per_mpiproc) != number_engines: raise click.BadParameter( f'{process_class.__name__} has {number_engines} engine steps, so requires {number_engines} values', param_hint='--number-cores-per-mpiproc') if wallclock_seconds is None: wallclock_seconds = [1 * 3600] * number_engines if len(wallclock_seconds) != number_engines: raise click.BadParameter( f'{process_class.__name__} has {number_engines} engine steps, so requires {number_engines} values', param_hint='--wallclock-seconds') if not generator.is_valid_protocol(protocol): protocols = generator.get_protocol_names() process_class_name = process_class.__name__ message = f'`{protocol}` is not implemented by `{process_class_name}` workflow: choose one of {protocols}' raise click.BadParameter(message, param_hint='protocol') if show_engines: for engine, port in generator.spec().inputs['engines'].items(): click.secho(engine, fg='red', bold=True) click.echo(f'Required code plugin: {port["code"].entry_point}') click.echo(f'Engine description: {port.help}') return validate_engine_options(engine_options, generator.spec().inputs['engines']) engines = {} for index, engine in enumerate(generator.spec().inputs['engines']): port = generator.spec().inputs['engines'][engine] entry_point = port['code'].code_entry_point code = utils.get_code_from_list_or_database(codes or [], entry_point) if code is None: raise click.UsageError( f'could not find a configured code for the plugin `{entry_point}`. ' 'Either provide it with the -X option or make sure such a code is configured in the DB.' ) all_options = { 'resources': { 'num_machines': number_machines[index], }, 'max_wallclock_seconds': wallclock_seconds[index], } all_options.update(engine_options.get(engine, {})) engines[engine] = {'code': code.full_label, 'options': all_options} if number_mpi_procs_per_machine is not None: engines[engine]['options']['resources'][ 'num_mpiprocs_per_machine'] = number_mpi_procs_per_machine[ index] if number_mpi_procs_per_machine[index] > 1: engines[engine]['options']['withmpi'] = True if number_cores_per_mpiproc is not None: engines[engine]['options']['resources'][ 'num_cores_per_mpiproc'] = number_cores_per_mpiproc[index] inputs = { 'structure': structure, 'generator_inputs': { 'engines': engines, 'protocol': protocol, 'relax_type': relax_type, 'electronic_type': electronic_type, 'spin_type': spin_type, }, 'sub_process_class': get_entry_point_name_from_class(process_class).name, } if threshold_forces is not None: inputs['generator_inputs']['threshold_forces'] = threshold_forces if threshold_stress is not None: inputs['generator_inputs']['threshold_stress'] = threshold_stress if magnetization_per_site is not None: inputs['generator_inputs'][ 'magnetization_per_site'] = magnetization_per_site utils.launch_process(EquationOfStateWorkChain, daemon, **inputs)