diff --git a/aiida_lammps/calculations/base.py b/aiida_lammps/calculations/base.py index e739aaf..730bc23 100644 --- a/aiida_lammps/calculations/base.py +++ b/aiida_lammps/calculations/base.py @@ -47,12 +47,6 @@ class LammpsBaseCalculation(CalcJob): @classmethod def define(cls, spec): super().define(spec) - spec.input( - "script", - valid_type=orm.SinglefileData, - required=False, - help="Complete input script to use. If specified, `structure`, `potential` and `parameters` are ignored.", - ) spec.input( "structure", valid_type=orm.StructureData, @@ -126,7 +120,6 @@ def define(cls, spec): default=cls._DEFAULT_RESTART_FILENAME, ) spec.inputs["metadata"]["options"]["parser_name"].default = cls._DEFAULT_PARSER - spec.inputs.validator = cls.validate_inputs spec.output( "results", @@ -213,17 +206,6 @@ def define(cls, spec): message="error parsing the final variable file has failed.", ) - @classmethod - def validate_inputs(cls, value, ctx): - """Validate the top-level inputs namespace.""" - if "script" not in value and any( - key not in value for key in ("structure", "potential", "parameters") - ): - return ( - "Unless `script` is specified the inputs `structure`, `potential` and " - "`parameters` have to be specified." - ) - @classmethod def validate_settings(cls, value, ctx): """Validate the ``settings`` input.""" @@ -299,38 +281,34 @@ def prepare_for_submission(self, folder): retrieve_list += restart_data.get("retrieve_list", []) retrieve_temporary_list += restart_data.get("retrieve_temporary_list", []) - if "script" in self.inputs: - input_filecontent = self.inputs.script.get_content() - else: + # Generate the content of the structure file based on the input + # structure + structure_filecontent, _ = generate_lammps_structure( + self.inputs.structure, + self.inputs.potential.atom_style, + ) - # Generate the content of the structure file based on the input - # structure - structure_filecontent, _ = generate_lammps_structure( - self.inputs.structure, - self.inputs.potential.atom_style, - ) + # Get the name of the structure file and write it to the remote folder + _structure_filename = self.inputs.metadata.options.structure_filename - # Get the name of the structure file and write it to the remote folder - _structure_filename = self.inputs.metadata.options.structure_filename - - with folder.open(_structure_filename, "w") as handle: - handle.write(structure_filecontent) - - # Write the potential to the remote folder - with folder.open(self._DEFAULT_POTENTIAL_FILENAME, "w") as handle: - handle.write(self.inputs.potential.get_content()) - - # Write the input file content. This function will also check the - # sanity of the passed parameters when comparing it to a schema - input_filecontent = generate_input_file( - potential=self.inputs.potential, - structure=self.inputs.structure, - parameters=_parameters, - restart_filename=_restart_filename, - trajectory_filename=_trajectory_filename, - variables_filename=_variables_filename, - read_restart_filename=_read_restart_filename, - ) + with folder.open(_structure_filename, "w") as handle: + handle.write(structure_filecontent) + + # Write the potential to the remote folder + with folder.open(self._DEFAULT_POTENTIAL_FILENAME, "w") as handle: + handle.write(self.inputs.potential.get_content()) + + # Write the input file content. This function will also check the + # sanity of the passed parameters when comparing it to a schema + input_filecontent = generate_input_file( + potential=self.inputs.potential, + structure=self.inputs.structure, + parameters=_parameters, + restart_filename=_restart_filename, + trajectory_filename=_trajectory_filename, + variables_filename=_variables_filename, + read_restart_filename=_read_restart_filename, + ) # Get the name of the input file, and write it to the remote folder _input_filename = self.inputs.metadata.options.input_filename diff --git a/aiida_lammps/parsers/base.py b/aiida_lammps/parsers/base.py index 1f59a00..fb5501e 100644 --- a/aiida_lammps/parsers/base.py +++ b/aiida_lammps/parsers/base.py @@ -92,8 +92,7 @@ def parse(self, **kwargs): final_variables = None variables_filename = self.node.get_option("variables_filename") if variables_filename not in list_of_files: - if "script" not in self.node.inputs: - return self.exit_codes.ERROR_FINAL_VARIABLE_FILE_MISSING + return self.exit_codes.ERROR_FINAL_VARIABLE_FILE_MISSING else: final_variables = parse_final_data( file_contents=self.node.outputs.retrieved.base.repository.get_object_content( @@ -120,8 +119,7 @@ def parse(self, **kwargs): # check trajectory file trajectory_filename = self.node.get_option("trajectory_filename") if trajectory_filename not in list_of_files: - if "script" not in self.node.inputs: - return self.exit_codes.ERROR_TRAJECTORY_FILE_MISSING + return self.exit_codes.ERROR_TRAJECTORY_FILE_MISSING else: with self.node.outputs.retrieved.base.repository.open( trajectory_filename diff --git a/tests/input_files/parsers/log_alt.lammps b/tests/parsers/fixtures/raw/alt/lammps.log similarity index 97% rename from tests/input_files/parsers/log_alt.lammps rename to tests/parsers/fixtures/raw/alt/lammps.log index 07e08bb..58e4920 100644 --- a/tests/input_files/parsers/log_alt.lammps +++ b/tests/parsers/fixtures/raw/alt/lammps.log @@ -27,9 +27,9 @@ Setting up Verlet run ... Current step : 0 Time step : 0.005 Per MPI rank memory allocation (min/avg/max) = 2.039 | 2.065 | 2.082 Mbytes - Step Temp E_pair E_mol TotEng Press - 0 1.44 -6.7733681 0 -4.6133724 -5.0196717 - 1000 0.70380068 -5.6761498 0 -4.6204508 0.70370101 + Step Temp E_pair E_mol TotEng Press + 0 1.44 -6.7733681 0 -4.6133724 -5.0196717 + 1000 0.70380068 -5.6761498 0 -4.6204508 0.70370101 Loop time of 3.07792 on 120 procs for 1000 steps with 500000 atoms Performance: 140354.357 tau/day, 324.894 timesteps/s, 162.447 Matom-step/s diff --git a/tests/parsers/test_raw.py b/tests/parsers/test_raw.py index 89b0c3d..9ba97e2 100644 --- a/tests/parsers/test_raw.py +++ b/tests/parsers/test_raw.py @@ -1,10 +1,7 @@ """Tests for the :mod:`aiida_lammps.parsers.raw` module.""" # pylint: disable=redefined-outer-name -from aiida.orm import SinglefileData from aiida.plugins import ParserFactory -from aiida_lammps.calculations.raw import LammpsRawCalculation - def test_default(generate_calc_job_node, data_regression): """Test parsing a default output case.""" @@ -15,3 +12,14 @@ def test_default(generate_calc_job_node, data_regression): assert calcfunction.is_finished, calcfunction.exception assert calcfunction.is_finished_ok, calcfunction.exit_message data_regression.check({"results": results["results"].get_dict()}) + + +def test_alt_timing_info(generate_calc_job_node, data_regression): + """Test parsing an alt output case.""" + node = generate_calc_job_node("lammps.raw", "alt") + parser = ParserFactory("lammps.raw") + results, calcfunction = parser.parse_from_node(node, store_provenance=False) + + assert calcfunction.is_finished, calcfunction.exception + assert calcfunction.is_finished_ok, calcfunction.exit_message + data_regression.check({"results": results["results"].get_dict()}) diff --git a/tests/parsers/test_raw/test_alt_timing_info.yml b/tests/parsers/test_raw/test_alt_timing_info.yml new file mode 100644 index 0000000..5fe7d71 --- /dev/null +++ b/tests/parsers/test_raw/test_alt_timing_info.yml @@ -0,0 +1,15 @@ +results: + compute_variables: + bin: kk/device + bins: + - 60 + - 60 + - 60 + binsize: 1.4 + ghost_atom_cutoff: 2.8 + master_list_distance_cutoff: 2.8 + max_neighbors_atom: 2000 + steps_per_second: 324.894 + total_wall_time: 0:00:03 + total_wall_time_seconds: 3 + units_style: lattice diff --git a/tests/test_calculations.py b/tests/test_calculations.py index f42e975..6922532 100644 --- a/tests/test_calculations.py +++ b/tests/test_calculations.py @@ -137,40 +137,6 @@ def test_lammps_base( assert sub_value == _step_data[sub_key], _msg -def test_lammps_base_script(generate_calc_job, aiida_local_code_factory): - """Test the ``LammpsBaseCalculation`` with the ``script`` input.""" - from aiida_lammps.calculations.base import LammpsBaseCalculation - - inputs = { - "code": aiida_local_code_factory("lammps.base", "bash"), - "metadata": {"options": {"resources": {"num_machines": 1}}}, - } - - with pytest.raises( - ValueError, - match=r"Unless `script` is specified the inputs .* have to be specified.", - ): - generate_calc_job("lammps.base", inputs) - - content = textwrap.dedent( - """ - "velocity all create 1.44 87287 loop geom - "pair_style lj/cut 2.5 - "pair_coeff 1 1 1.0 1.0 2.5 - "neighbor 0.3 bin - "neigh_modify delay 0 every 20 check no - "fix 1 all nve - "run 10000 - """ - ) - stream = io.StringIO(content) - script = orm.SinglefileData(stream) - - inputs["script"] = script - tmp_path, calc_info = generate_calc_job("lammps.base", inputs) - assert (tmp_path / LammpsBaseCalculation._INPUT_FILENAME).read_text() == content - - @pytest.mark.lammps_call @pytest.mark.parametrize( "parameters,restart_parameters", @@ -342,13 +308,20 @@ def test_lammps_base_settings_invalid(generate_calc_job, aiida_local_code_factor generate_calc_job("lammps.base", inputs) -def test_lammps_base_settings(generate_calc_job, aiida_local_code_factory): +def test_lammps_base_settings( + generate_calc_job, + aiida_local_code_factory, + minimize_parameters, + get_potential_fe_eam, + generate_structure, +): """Test the ``LammpsBaseCalculation`` with the ``settings`` input.""" - from aiida_lammps.calculations.base import LammpsBaseCalculation inputs = { "code": aiida_local_code_factory("lammps.base", "bash"), - "script": orm.SinglefileData(io.StringIO("")), + "parameters": orm.Dict(minimize_parameters), + "potential": get_potential_fe_eam, + "structure": generate_structure, "settings": orm.Dict({"additional_cmdline_params": ["--option", "value"]}), "metadata": {"options": {"resources": {"num_machines": 1}}}, } diff --git a/tests/test_parsers.py b/tests/test_parsers.py index d7a8da7..a339d95 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -47,76 +47,6 @@ def get_traj_force(): ) -def test_lammps_base(db_test_app, data_regression): - """Check if the log file is produced during calculation.""" - filename = os.path.join( - TEST_DIR, - "input_files", - "parsers", - "log.lammps", - ) - retrieved = FolderData() - retrieved.base.repository.put_object_from_file(filename, "log.lammps") - retrieved.base.repository.put_object_from_filelike( - io.StringIO(""), "_scheduler-stdout.txt" - ) - retrieved.base.repository.put_object_from_filelike( - io.StringIO(""), "_scheduler-stderr.txt" - ) - inputs = {"script": SinglefileData(io.StringIO(""))} - calc_node = db_test_app.generate_calcjob_node( - "lammps.base", retrieved, inputs=inputs - ) - parser = ParserFactory("lammps.base") - with db_test_app.sandbox_folder() as temp_path: - ( - results, - calcfunction, - ) = parser.parse_from_node( # pylint: disable=unused-variable - calc_node, - retrieved_temporary_folder=temp_path.abspath, - ) - - assert calcfunction.is_finished_ok - assert "results" in calcfunction.outputs - data_regression.check({"results": calcfunction.outputs.results.get_dict()}) - - -def test_lammps_base_timing_info(db_test_app, data_regression): - """Test that the timing performance is properly parsed.""" - filename = os.path.join( - TEST_DIR, - "input_files", - "parsers", - "log_alt.lammps", - ) - retrieved = FolderData() - retrieved.base.repository.put_object_from_file(filename, "log.lammps") - retrieved.base.repository.put_object_from_filelike( - io.StringIO(""), "_scheduler-stdout.txt" - ) - retrieved.base.repository.put_object_from_filelike( - io.StringIO(""), "_scheduler-stderr.txt" - ) - inputs = {"script": SinglefileData(io.StringIO(""))} - calc_node = db_test_app.generate_calcjob_node( - "lammps.base", retrieved, inputs=inputs - ) - parser = ParserFactory("lammps.base") - with db_test_app.sandbox_folder() as temp_path: - ( - results, - calcfunction, - ) = parser.parse_from_node( # pylint: disable=unused-variable - calc_node, - retrieved_temporary_folder=temp_path.abspath, - ) - - assert calcfunction.is_finished_ok - assert "results" in calcfunction.outputs - data_regression.check({"results": calcfunction.outputs.results.get_dict()}) - - def test_parser_log(data_regression): """ Test the parser for the ``log.lammps`` file.