Repository: imcs-compsim/meshpy
Pull request #321: Remove inheritance of InputFile on Mesh
Author: login: davidrudlstorfer
Created: 2025-04-11T15:25:17Z | Merged: 2025-05-06T07:07:21Z

I decided to add one additional step before moving the input file to fourcipp - namely the removal of the inheritance of the `InputFile` on `Mesh`

The overall code changes are pretty minimal - but major changes must occur to the testing framework.

Following this PR - the change to `fourcipp` should be really straight forward


Diff:
diff --git a/src/meshpy/four_c/dbc_monitor.py b/src/meshpy/four_c/dbc_monitor.py
index 9787eb0c..e9ea0a47 100644
--- a/src/meshpy/four_c/dbc_monitor.py
+++ b/src/meshpy/four_c/dbc_monitor.py
@@ -183,10 +183,10 @@ def add_point_neuman_condition_to_input_file(
 
     # Add the function to the input file, if they are not previously added.
     for function in function_array:
-        input_file.add(function)
+        input_file.mesh.add(function)
 
     # Create GeometrySet with nodes.
-    mesh_nodes = [input_file.nodes[i_node] for i_node in nodes]
+    mesh_nodes = [input_file.mesh.nodes[i_node] for i_node in nodes]
     geo = _GeometrySet(mesh_nodes)
 
     # Create the Boundary Condition.
@@ -200,7 +200,7 @@ def add_point_neuman_condition_to_input_file(
         },
         bc_type=_mpy.bc.neumann,
     )
-    input_file.add(bc)
+    input_file.mesh.add(bc)
 
 
 def dbc_monitor_to_input_all_values(
@@ -322,7 +322,7 @@ def dbc_monitor_to_input_all_values(
             )
 
             # add the function to the input array
-            input_file.add(fun)
+            input_file.mesh.add(fun)
 
             # store function
             functions.append(fun)
diff --git a/src/meshpy/four_c/input_file.py b/src/meshpy/four_c/input_file.py
index a384afc8..9d5298e9 100644
--- a/src/meshpy/four_c/input_file.py
+++ b/src/meshpy/four_c/input_file.py
@@ -128,7 +128,7 @@ def _get_yaml_geometry_sets(
     return geometry_sets_in_this_section
 
 
-class InputFile(_Mesh):
+class InputFile:
     """An item that represents a complete 4C input file."""
 
     # Define the names of sections and boundary conditions in the input file.
@@ -206,7 +206,7 @@ def __init__(self, *, yaml_file: _Optional[_Path] = None, cubit=None):
                 into this input file.
         """
 
-        super().__init__()
+        self.mesh = _Mesh()
 
         # Everything that is not a full MeshPy object is stored here, e.g., parameters
         # and imported nodes/elements/materials/...
@@ -345,7 +345,7 @@ def add(self, *args, **kwargs):
                 self.add_section(args[0], **kwargs)
                 return
 
-        super().add(*args, **kwargs)
+        self.mesh.add(*args, **kwargs)
 
     def add_section(self, section, *, option_overwrite=False):
         """Add a section to the object.
@@ -462,7 +462,7 @@ def get_dict_to_dump(
 
         # Perform some checks on the mesh.
         if _mpy.check_overlapping_elements:
-            self.check_overlapping_elements()
+            self.mesh.check_overlapping_elements()
 
         # The base dictionary we use here is the one that already exists.
         # This one might already contain mesh sections - stored in pure
@@ -619,27 +619,27 @@ def _dump_mesh_items(yaml_dict, section_name, data_list):
                     raise TypeError(f"Could not dump {item}")
 
         # Add sets from couplings and boundary conditions to a temp container.
-        self.unlink_nodes()
+        self.mesh.unlink_nodes()
         start_indices_geometry_set = _get_global_start_geometry_set(yaml_dict)
-        mesh_sets = self.get_unique_geometry_sets(
+        mesh_sets = self.mesh.get_unique_geometry_sets(
             geometry_set_start_indices=start_indices_geometry_set
         )
 
         # Assign global indices to all entries.
         start_index_nodes = _get_global_start_node(yaml_dict)
-        _set_i_global(self.nodes, start_index=start_index_nodes)
+        _set_i_global(self.mesh.nodes, start_index=start_index_nodes)
         start_index_elements = _get_global_start_element(yaml_dict)
-        _set_i_global_elements(self.elements, start_index=start_index_elements)
+        _set_i_global_elements(self.mesh.elements, start_index=start_index_elements)
         start_index_materials = _get_global_start_material(yaml_dict)
-        _set_i_global(self.materials, start_index=start_index_materials)
+        _set_i_global(self.mesh.materials, start_index=start_index_materials)
         start_index_functions = _get_global_start_function(yaml_dict)
-        _set_i_global(self.functions, start_index=start_index_functions)
+        _set_i_global(self.mesh.functions, start_index=start_index_functions)
 
         # Add material data to the input file.
-        _dump_mesh_items(yaml_dict, "MATERIALS", self.materials)
+        _dump_mesh_items(yaml_dict, "MATERIALS", self.mesh.materials)
 
         # Add the functions.
-        for function in self.functions:
+        for function in self.mesh.functions:
             yaml_dict[f"FUNCT{function.i_global}"] = function.dump_to_list()
 
         # If there are couplings in the mesh, set the link between the nodes
@@ -647,8 +647,8 @@ def _dump_mesh_items(yaml_dict, section_name, data_list):
         # depending on the type of the connected beam element.
         def get_number_of_coupling_conditions(key):
             """Return the number of coupling conditions in the mesh."""
-            if (key, _mpy.geo.point) in self.boundary_conditions.keys():
-                return len(self.boundary_conditions[key, _mpy.geo.point])
+            if (key, _mpy.geo.point) in self.mesh.boundary_conditions.keys():
+                return len(self.mesh.boundary_conditions[key, _mpy.geo.point])
             else:
                 return 0
 
@@ -657,10 +657,10 @@ def get_number_of_coupling_conditions(key):
             + get_number_of_coupling_conditions(_mpy.bc.point_coupling_penalty)
             > 0
         ):
-            self.set_node_links()
+            self.mesh.set_node_links()
 
         # Add the boundary conditions.
-        for (bc_key, geom_key), bc_list in self.boundary_conditions.items():
+        for (bc_key, geom_key), bc_list in self.mesh.boundary_conditions.items():
             if len(bc_list) > 0:
                 section_name = (
                     bc_key
@@ -670,7 +670,7 @@ def get_number_of_coupling_conditions(key):
                 _dump_mesh_items(yaml_dict, section_name, bc_list)
 
         # Add additional element sections, e.g., for NURBS knot vectors.
-        for element in self.elements:
+        for element in self.mesh.elements:
             element.dump_element_specific_section(yaml_dict)
 
         # Add the geometry sets.
@@ -679,8 +679,8 @@ def get_number_of_coupling_conditions(key):
                 _dump_mesh_items(yaml_dict, self.geometry_set_names[geom_key], item)
 
         # Add the nodes and elements.
-        _dump_mesh_items(yaml_dict, "NODE COORDS", self.nodes)
-        _dump_mesh_items(yaml_dict, "STRUCTURE ELEMENTS", self.elements)
+        _dump_mesh_items(yaml_dict, "NODE COORDS", self.mesh.nodes)
+        _dump_mesh_items(yaml_dict, "STRUCTURE ELEMENTS", self.mesh.elements)
 
         return yaml_dict
 
diff --git a/tests/conftest.py b/tests/conftest.py
index 99827fb7..a5f6f09b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -38,6 +38,7 @@
 from vtk_utils.compare_grids import compare_grids
 
 from meshpy.core.conf import mpy
+from meshpy.core.mesh import Mesh
 from meshpy.four_c.input_file import InputFile
 from meshpy.four_c.yaml_dumper import MeshPyDumper as _MeshPyDumper
 from meshpy.utils.environment import fourcipp_is_available
@@ -274,8 +275,8 @@ def assert_results_equal(get_string, tmp_path, current_test_name) -> Callable:
     """
 
     def _assert_results_equal(
-        reference: Union[Path, str, dict],
-        result: Union[Path, str, dict, InputFile],
+        reference: Union[Path, str, dict, InputFile, Mesh],
+        result: Union[Path, str, dict, InputFile, Mesh],
         rtol: Optional[float] = None,
         atol: Optional[float] = None,
         input_file_kwargs: dict = {"add_header_information": False, "check_nox": False},
@@ -331,10 +332,20 @@ def get_dictionary(data) -> dict:
             compare_dicts(reference_dict, result_dict, rtol=rtol, atol=atol)
             return
 
-        if isinstance(reference, InputFile) or isinstance(result, InputFile):
+        if isinstance(reference, (InputFile, Mesh)) or isinstance(
+            result, (InputFile, Mesh)
+        ):
 
             def get_dictionary(data) -> dict:
                 """Get the dictionary representation of the data object."""
+
+                # Internally convert Mesh to InputFile to allow for simple comparison via dictionary
+                # TODO this should be improved in the future
+                if isinstance(data, Mesh):
+                    input_file = InputFile()
+                    input_file.add(data)
+                    data = input_file
+
                 if isinstance(data, InputFile):
                     if fourcipp_is_available():
                         raise ValueError(
diff --git a/tests/reference-files/test_meshpy_comments_in_solid.4C.yaml b/tests/reference-files/test_meshpy_comments_in_solid.4C.yaml
deleted file mode 100644
index e2228724..00000000
--- a/tests/reference-files/test_meshpy_comments_in_solid.4C.yaml
+++ /dev/null
@@ -1,159 +0,0 @@
-DESIGN POINT DIRICH CONDITIONS:
-  - E: 1
-    FUNCT:
-      - 0
-      - 0
-      - 0
-    NUMDOF: 3
-    ONOFF:
-      - 1
-      - 1
-      - 1
-    VAL:
-      - 1
-      - 0
-      - 0
-DESIGN POINT NEUMANN CONDITIONS:
-  - E: 2
-    FUNCT:
-      - 0
-      - 0
-      - 0
-    NUMDOF: 3
-    ONOFF:
-      - 1
-      - 1
-      - 1
-    VAL:
-      - 2
-      - 0
-      - 0
-DESIGN SURF DIRICH CONDITIONS:
-  - E: 1
-    FUNCT:
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-    NUMDOF: 6
-    ONOFF:
-      - 1
-      - 1
-      - 1
-      - 0
-      - 0
-      - 0
-    VAL:
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-  - E: 2
-    FUNCT:
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-    NUMDOF: 6
-    ONOFF:
-      - 1
-      - 1
-      - 1
-      - 0
-      - 0
-      - 0
-    VAL:
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-DISCRETISATION:
-  NUMALEDIS: 0
-  NUMFLUIDDIS: 0
-  NUMTHERMDIS: 0
-DNODE-NODE TOPOLOGY:
-  - NODE 17 DNODE 1
-  - NODE 19 DNODE 2
-DSURF-NODE TOPOLOGY:
-  - NODE 1 DSURFACE 1
-  - NODE 4 DSURFACE 1
-  - NODE 5 DSURFACE 1
-  - NODE 8 DSURFACE 1
-  - NODE 13 DSURFACE 2
-  - NODE 14 DSURFACE 2
-  - NODE 15 DSURFACE 2
-  - NODE 16 DSURFACE 2
-IO:
-  VERBOSITY: Standard
-IO/RUNTIME VTK OUTPUT:
-  INTERVAL_STEPS: 1
-IO/RUNTIME VTK OUTPUT/STRUCTURE:
-  DISPLACEMENT: true
-  OUTPUT_STRUCTURE: true
-MATERIALS:
-  - MAT: 1
-    MAT_Struct_StVenantKirchhoff:
-      DENS: 7.8e-06
-      NUE: 0
-      YOUNG: 1e+09
-  - MAT: 2
-    MAT_BeamReissnerElastHyper:
-      CROSSAREA: 3.141592653589793
-      DENS: 0.0
-      MOMIN2: 0.7853981633974483
-      MOMIN3: 0.7853981633974483
-      MOMINPOL: 1.5707963267948966
-      POISSONRATIO: 0.0
-      SHEARCORR: 1
-      YOUNG: -1.0
-NODE COORDS:
-  - NODE 1 COORD -5.0000000000000003e-02 -5.0000000000000003e-02 0.0000000000000000e+00
-  - NODE 2 COORD -5.0000000000000003e-02 -5.0000000000000003e-02 -3.3333333333333331e-01
-  - NODE 3 COORD -5.0000000000000003e-02 5.0000000000000003e-02 -3.3333333333333331e-01
-  - NODE 4 COORD -5.0000000000000003e-02 5.0000000000000003e-02 0.0000000000000000e+00
-  - NODE 5 COORD 5.0000000000000003e-02 -5.0000000000000003e-02 0.0000000000000000e+00
-  - NODE 6 COORD 5.0000000000000003e-02 -5.0000000000000003e-02 -3.3333333333333331e-01
-  - NODE 7 COORD 5.0000000000000003e-02 5.0000000000000003e-02 -3.3333333333333331e-01
-  - NODE 8 COORD 5.0000000000000003e-02 5.0000000000000003e-02 0.0000000000000000e+00
-  - NODE 9 COORD -5.0000000000000003e-02 -5.0000000000000003e-02 -6.6666666666666663e-01
-  - NODE 10 COORD -5.0000000000000003e-02 5.0000000000000003e-02 -6.6666666666666663e-01
-  - NODE 11 COORD 5.0000000000000003e-02 -5.0000000000000003e-02 -6.6666666666666663e-01
-  - NODE 12 COORD 5.0000000000000003e-02 5.0000000000000003e-02 -6.6666666666666663e-01
-  - NODE 13 COORD -5.0000000000000003e-02 -5.0000000000000003e-02 -1.0000000000000000e+00
-  - NODE 14 COORD -5.0000000000000003e-02 5.0000000000000003e-02 -1.0000000000000000e+00
-  - NODE 15 COORD 5.0000000000000003e-02 -5.0000000000000003e-02 -1.0000000000000000e+00
-  - NODE 16 COORD 5.0000000000000003e-02 5.0000000000000003e-02 -1.0000000000000000e+00
-  - NODE 17 COORD 0.0 0.0 0.0
-  - NODE 18 COORD 0.5 1.0 1.5
-  - NODE 19 COORD 1.0 2.0 3.0
-PROBLEM TYPE:
-  PROBLEMTYPE: Structure
-SOLVER 1:
-  NAME: Structure_Solver
-  SOLVER: UMFPACK
-STRUCTURAL DYNAMIC:
-  DYNAMICTYPE: Statics
-  INT_STRATEGY: Standard
-  LINEAR_SOLVER: 1
-  MAXITER: 20
-  MAXTIME: 1
-  NUMSTEP: 1
-  RESTARTEVERY: 5
-  TIMESTEP: 1
-  TOLDISP: 1e-11
-  TOLRES: 1e-05
-STRUCTURE ELEMENTS:
-  - 1 SOLIDH8 HEX8 1 2 3 4 5 6 7 8 MAT 1 KINEM nonlinear EAS none
-  - 2 SOLIDH8 HEX8 2 9 10 3 6 11 12 7 MAT 1 KINEM nonlinear EAS none
-  - 3 SOLIDH8 HEX8 9 13 14 10 11 15 16 12 MAT 1 KINEM nonlinear EAS none
-  - 4 BEAM3R HERM2LINE3 17 19 18 MAT 2 TRIADS -0.3516761033122818 -1.213974487414885 0.4879245294772592 -0.3516761033122818 -1.213974487414885 0.4879245294772592 -0.3516761033122818 -1.213974487414885 0.4879245294772592
-TITLE:
-  - created by pre_exodus
diff --git a/tests/reference-files/test_meshpy_comments_in_solid_initial.4C.yaml b/tests/reference-files/test_meshpy_comments_in_solid_initial.4C.yaml
deleted file mode 100644
index 19d4c353..00000000
--- a/tests/reference-files/test_meshpy_comments_in_solid_initial.4C.yaml
+++ /dev/null
@@ -1,112 +0,0 @@
-DESIGN SURF DIRICH CONDITIONS:
-  - E: 1
-    FUNCT:
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-    NUMDOF: 6
-    ONOFF:
-      - 1
-      - 1
-      - 1
-      - 0
-      - 0
-      - 0
-    VAL:
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-  - E: 2
-    FUNCT:
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-    NUMDOF: 6
-    ONOFF:
-      - 1
-      - 1
-      - 1
-      - 0
-      - 0
-      - 0
-    VAL:
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-      - 0
-DISCRETISATION:
-  NUMALEDIS: 0
-  NUMFLUIDDIS: 0
-  NUMTHERMDIS: 0
-DSURF-NODE TOPOLOGY:
-  - NODE 1 DSURFACE 1
-  - NODE 4 DSURFACE 1
-  - NODE 5 DSURFACE 1
-  - NODE 8 DSURFACE 1
-  - NODE 13 DSURFACE 2
-  - NODE 14 DSURFACE 2
-  - NODE 15 DSURFACE 2
-  - NODE 16 DSURFACE 2
-IO:
-  VERBOSITY: Standard
-IO/RUNTIME VTK OUTPUT:
-  INTERVAL_STEPS: 1
-IO/RUNTIME VTK OUTPUT/STRUCTURE:
-  DISPLACEMENT: true
-  OUTPUT_STRUCTURE: true
-MATERIALS:
-  - MAT: 1
-    MAT_Struct_StVenantKirchhoff:
-      DENS: 7.8e-06
-      NUE: 0
-      YOUNG: 1e+09
-NODE COORDS:
-  - NODE 1 COORD -5.0000000000000003e-02 -5.0000000000000003e-02 0.0000000000000000e+00
-  - NODE 2 COORD -5.0000000000000003e-02 -5.0000000000000003e-02 -3.3333333333333331e-01
-  - NODE 3 COORD -5.0000000000000003e-02 5.0000000000000003e-02 -3.3333333333333331e-01
-  - NODE 4 COORD -5.0000000000000003e-02 5.0000000000000003e-02 0.0000000000000000e+00
-  - NODE 5 COORD 5.0000000000000003e-02 -5.0000000000000003e-02 0.0000000000000000e+00
-  - NODE 6 COORD 5.0000000000000003e-02 -5.0000000000000003e-02 -3.3333333333333331e-01
-  - NODE 7 COORD 5.0000000000000003e-02 5.0000000000000003e-02 -3.3333333333333331e-01
-  - NODE 8 COORD 5.0000000000000003e-02 5.0000000000000003e-02 0.0000000000000000e+00
-  - NODE 9 COORD -5.0000000000000003e-02 -5.0000000000000003e-02 -6.6666666666666663e-01
-  - NODE 10 COORD -5.0000000000000003e-02 5.0000000000000003e-02 -6.6666666666666663e-01
-  - NODE 11 COORD 5.0000000000000003e-02 -5.0000000000000003e-02 -6.6666666666666663e-01
-  - NODE 12 COORD 5.0000000000000003e-02 5.0000000000000003e-02 -6.6666666666666663e-01
-  - NODE 13 COORD -5.0000000000000003e-02 -5.0000000000000003e-02 -1.0000000000000000e+00
-  - NODE 14 COORD -5.0000000000000003e-02 5.0000000000000003e-02 -1.0000000000000000e+00
-  - NODE 15 COORD 5.0000000000000003e-02 -5.0000000000000003e-02 -1.0000000000000000e+00
-  - NODE 16 COORD 5.0000000000000003e-02 5.0000000000000003e-02 -1.0000000000000000e+00
-PROBLEM TYPE:
-  PROBLEMTYPE: Structure
-SOLVER 1:
-  NAME: Structure_Solver
-  SOLVER: UMFPACK
-STRUCTURAL DYNAMIC:
-  DYNAMICTYPE: Statics
-  INT_STRATEGY: Standard
-  LINEAR_SOLVER: 1
-  MAXITER: 20
-  MAXTIME: 1
-  NUMSTEP: 1
-  RESTARTEVERY: 5
-  TIMESTEP: 1
-  TOLDISP: 1e-11
-  TOLRES: 1e-05
-STRUCTURE ELEMENTS:
-  - 1 SOLIDH8 HEX8 1 2 3 4 5 6 7 8 MAT 1 KINEM nonlinear EAS none
-  - 2 SOLIDH8 HEX8 2 9 10 3 6 11 12 7 MAT 1 KINEM nonlinear EAS none
-  - 3 SOLIDH8 HEX8 9 13 14 10 11 15 16 12 MAT 1 KINEM nonlinear EAS none
-TITLE:
-  - created by pre_exodus
diff --git a/tests/test_cosserat_curve.py b/tests/test_cosserat_curve.py
index 88ea5bd3..bc6f2a4b 100644
--- a/tests/test_cosserat_curve.py
+++ b/tests/test_cosserat_curve.py
@@ -60,7 +60,8 @@ def create_beam_solid_input_file(get_corresponding_reference_file_path):
         yaml_file=get_corresponding_reference_file_path(
             reference_file_base_name="test_cosserat_curve_mesh"
         )
-    )
+    ).mesh
+
     create_beam_mesh_helix(
         mesh,
         Beam3rHerm2Line3,
@@ -205,7 +206,6 @@ def test_cosserat_curve_mesh_warp(
             Rotation([0, 0, 1], -0.5 * np.pi) * Rotation([0, 1, 0], -0.5 * np.pi)
         ),
     )
-
     assert_results_equal(get_corresponding_reference_file_path(), mesh, rtol=1e-10)
 
 
diff --git a/tests/test_four_c.py b/tests/test_four_c.py
index dbfcf452..b42c6471 100644
--- a/tests/test_four_c.py
+++ b/tests/test_four_c.py
@@ -28,6 +28,7 @@
 from meshpy.core.boundary_condition import BoundaryCondition
 from meshpy.core.conf import mpy
 from meshpy.core.geometry_set import GeometrySet
+from meshpy.core.mesh import Mesh
 from meshpy.core.rotation import Rotation
 from meshpy.four_c.beam_interaction_conditions import add_beam_interaction_condition
 from meshpy.four_c.beam_potential import BeamPotential
@@ -162,7 +163,12 @@ def test_four_c_material_numbering(
             ]
         }
     )
-    input_file.add(MaterialReissner(youngs_modulus=1.0, radius=2.0))
+
+    mesh = Mesh()
+    mesh.add(MaterialReissner(youngs_modulus=1.0, radius=2.0))
+
+    input_file.add(mesh)
+
     assert_results_equal(get_corresponding_reference_file_path(), input_file)
 
 
@@ -172,7 +178,9 @@ def test_four_c_simulation_beam_potential_helix(
     """Test the correct creation of input files for simulations including beam
     to beam potential interactions."""
 
+    mesh = Mesh()
     input_file = InputFile()
+
     mat = MaterialReissner(youngs_modulus=1000, radius=0.5, shear_correction=1.0)
 
     # define function for line charge density
@@ -204,7 +212,7 @@ def test_four_c_simulation_beam_potential_helix(
 
     # create helix
     helix_set = create_beam_mesh_helix(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [0.0, 0.0, 1.0],
@@ -219,10 +227,10 @@ def test_four_c_simulation_beam_potential_helix(
     beampotential.add_potential_charge_condition(geometry_set=helix_set["line"])
 
     # Add boundary condition to bottom node
-    input_file.add(
+    mesh.add(
         BoundaryCondition(
             GeometrySet(
-                input_file.get_nodes_by_function(
+                mesh.get_nodes_by_function(
                     is_node_on_plane,
                     normal=[0, 0, 1],
                     origin_distance=0.0,
@@ -239,6 +247,8 @@ def test_four_c_simulation_beam_potential_helix(
         )
     )
 
+    input_file.add(mesh)
+
     assert_results_equal(get_corresponding_reference_file_path(), input_file)
 
 
@@ -255,7 +265,7 @@ def test_four_c_solid_shell_direction_detection(
         yaml_file=get_corresponding_reference_file_path(
             reference_file_base_name="test_create_cubit_input_solid_shell_blocks"
         )
-    )
+    ).mesh
     # Add a beam element to check the function also works with beam elements
     mat = MaterialReissner()
     create_beam_mesh_line(
@@ -273,7 +283,7 @@ def test_four_c_solid_shell_direction_detection(
         yaml_file=get_corresponding_reference_file_path(
             reference_file_base_name="test_create_cubit_input_solid_shell_dome"
         )
-    )
+    ).mesh
 
     # Test that the thickness version works
     mesh_dome = mesh_dome_original.copy()
@@ -336,22 +346,22 @@ def test_four_c_locsys_condition(
     material, and an additional line locsys condition.
     """
 
-    # Create the input file with function and material.
-    input_file = InputFile()
+    # Create the mesh.
+    mesh = Mesh()
 
     fun = Function([{"SYMBOLIC_FUNCTION_OF_SPACE_TIME": "t"}])
-    input_file.add(fun)
+    mesh.add(fun)
 
     mat = MaterialReissner()
-    input_file.add(mat)
+    mesh.add(mat)
 
     # Create the beam.
     beam_set = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, mat, [2.5, 2.5, 2.5], [4.5, 2.5, 2.5], n_el=1
+        mesh, Beam3rHerm2Line3, mat, [2.5, 2.5, 2.5], [4.5, 2.5, 2.5], n_el=1
     )
 
     # Add dirichlet boundary conditions.
-    input_file.add(
+    mesh.add(
         BoundaryCondition(
             beam_set["start"],
             {
@@ -364,7 +374,7 @@ def test_four_c_locsys_condition(
         )
     )
     # Add additional dirichlet boundary condition to check if combination with locsys condition works.
-    input_file.add(
+    mesh.add(
         BoundaryCondition(
             beam_set["end"],
             {
@@ -378,15 +388,15 @@ def test_four_c_locsys_condition(
     )
 
     # Add locsys condition with rotation
-    input_file.add(LocSysCondition(beam_set["end"], Rotation([0, 0, 1], 0.1)))
+    mesh.add(LocSysCondition(beam_set["end"], Rotation([0, 0, 1], 0.1)))
 
     # Add line Function with function array
 
     fun_2 = Function([{"SYMBOLIC_FUNCTION_OF_SPACE_TIME": "2.0*t"}])
-    input_file.add(fun_2)
+    mesh.add(fun_2)
 
     # Check if the LocSys condition is added correctly for a line with additional options.
-    input_file.add(
+    mesh.add(
         LocSysCondition(
             GeometrySet(beam_set["line"]),
             Rotation(
@@ -400,7 +410,7 @@ def test_four_c_locsys_condition(
     )
 
     # Check that the Locsys condition works with 3 given functions
-    input_file.add(
+    mesh.add(
         LocSysCondition(
             GeometrySet(beam_set["start"]),
             Rotation(
@@ -414,7 +424,7 @@ def test_four_c_locsys_condition(
     )
 
     # Compare with the reference solution.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_four_c_linear_time_transformation_scaling():
@@ -521,7 +531,7 @@ def test_four_c_add_beam_interaction_condition():
     correctly."""
 
     # Create the mesh.
-    mesh = InputFile()
+    mesh = Mesh()
 
     # Create Material.
     mat = MaterialReissner()
@@ -587,7 +597,7 @@ def test_four_c_beam_to_beam_contact(
     """Test the beam-to-beam contact boundary conditions."""
 
     # Create the mesh.
-    mesh = InputFile()
+    mesh = Mesh()
 
     # Create Material.
     mat = MaterialReissner()
@@ -629,64 +639,61 @@ def test_four_c_beam_to_solid(
 
     # Load a solid
     mpy.import_mesh_full = True
-    input_file = InputFile(
+    mesh = InputFile(
         yaml_file=get_corresponding_reference_file_path(
             reference_file_base_name="test_create_cubit_input_block"
         )
-    )
+    ).mesh
 
     # The yaml file already contains the beam-to-solid boundary conditions
     # for the solid. We don't need them in this test case, as we want to
     # create them again. Thus, we have to delete them here.
-    input_file.boundary_conditions[
+    mesh.boundary_conditions[
         (mpy.bc.beam_to_solid_volume_meshtying, mpy.geo.volume)
     ].clear()
-    input_file.boundary_conditions[
+    mesh.boundary_conditions[
         (mpy.bc.beam_to_solid_surface_meshtying, mpy.geo.surface)
     ].clear()
 
     # Get the geometry set objects representing the geometry from the cubit
     # file.
-    surface_set = input_file.geometry_sets[mpy.geo.surface][0]
-    volume_set = input_file.geometry_sets[mpy.geo.volume][0]
+    surface_set = mesh.geometry_sets[mpy.geo.surface][0]
+    volume_set = mesh.geometry_sets[mpy.geo.volume][0]
 
     # Add the beam
     material = MaterialReissner()
     beam_set_1 = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, material, [0, 0, 0], [0, 0, 1], n_el=1
+        mesh, Beam3rHerm2Line3, material, [0, 0, 0], [0, 0, 1], n_el=1
     )
     beam_set_2 = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, material, [0, 1, 0], [0, 1, 1], n_el=2
+        mesh, Beam3rHerm2Line3, material, [0, 1, 0], [0, 1, 1], n_el=2
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         volume_set,
         beam_set_1["line"],
         mpy.bc.beam_to_solid_volume_meshtying,
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         volume_set,
         beam_set_2["line"],
         mpy.bc.beam_to_solid_volume_meshtying,
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         surface_set,
         beam_set_2["line"],
         mpy.bc.beam_to_solid_surface_meshtying,
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         surface_set,
         beam_set_1["line"],
         mpy.bc.beam_to_solid_surface_meshtying,
     )
 
-    # Check results
-    print(get_corresponding_reference_file_path())
-    print(input_file)
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
     # If we try to add this the IDs won't match, because the next volume ID for
     # beam-to-surface coupling should be 0 (this one does not make sense, but
@@ -694,7 +701,7 @@ def test_four_c_beam_to_solid(
     # coupling is 2 (there are already two of these conditions).
     with pytest.raises(ValueError):
         add_beam_interaction_condition(
-            input_file,
+            mesh,
             volume_set,
             beam_set_1["line"],
             mpy.bc.beam_to_solid_surface_meshtying,
@@ -705,12 +712,12 @@ def test_four_c_beam_to_solid(
     # contains a volume set.
     with pytest.raises(KeyError):
         add_beam_interaction_condition(
-            input_file,
+            mesh,
             volume_set,
             beam_set_1["line"],
             mpy.bc.beam_to_solid_surface_contact,
         )
-        assert_results_equal(get_corresponding_reference_file_path(), input_file)
+        assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 @pytest.mark.parametrize(
@@ -725,6 +732,13 @@ def test_four_c_import_non_consecutive_geometry_sets(
 ):
     """Test that we can import non-consecutively numbered geometry sets."""
 
+    # TODO rework this test once the input file is refactored, i.e.
+    # input_file, mesh = InputFile(yaml_file=...)
+    # ... add different geometry sets to the mesh
+    # input_file.add(mesh)
+    # this way it is tested exhaustively rather than accessing the mesh
+    # in the input file during the beam mesh creation
+
     mpy.import_mesh_full = full_import
     input_file = InputFile(
         yaml_file=get_corresponding_reference_file_path(additional_identifier="input")
@@ -733,7 +747,12 @@ def test_four_c_import_non_consecutive_geometry_sets(
     material = MaterialReissner()
     for i in range(3):
         beam_set = create_beam_mesh_line(
-            input_file, Beam3rHerm2Line3, material, [i + 3, 0, 0], [i + 3, 0, 4], n_el=2
+            input_file.mesh,
+            Beam3rHerm2Line3,
+            material,
+            [i + 3, 0, 0],
+            [i + 3, 0, 4],
+            n_el=2,
         )
         input_file.add(beam_set)
 
diff --git a/tests/test_four_c_simulation.py b/tests/test_four_c_simulation.py
index 8e184f9f..968ba63d 100644
--- a/tests/test_four_c_simulation.py
+++ b/tests/test_four_c_simulation.py
@@ -88,11 +88,15 @@ def create_cantilever_model(n_steps, time_step=0.5):
         ]
     )
     input_file.add(ft)
+
+    mesh = Mesh()
     mat = MaterialReissner(youngs_modulus=100.0, radius=0.1)
     beam_set = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, mat, [0, 0, 0], [2, 0, 0], n_el=10
+        mesh, Beam3rHerm2Line3, mat, [0, 0, 0], [2, 0, 0], n_el=10
     )
 
+    input_file.add(mesh)
+
     return input_file, beam_set
 
 
@@ -313,14 +317,17 @@ def test_four_c_simulation_beam_and_solid_tube(
     material = MaterialReissner(youngs_modulus=1e9, radius=0.25, shear_correction=0.75)
     input_file.add(sin, cos, material)
 
+    # Create a mesh
+    mesh = Mesh()
+
     # Add a straight beam.
-    input_file.add(material)
+    mesh.add(material)
     cantilever_set = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, material, [2, 0, -5], [2, 0, 5], n_el=3
+        mesh, Beam3rHerm2Line3, material, [2, 0, -5], [2, 0, 5], n_el=3
     )
 
     # Add boundary conditions.
-    input_file.add(
+    mesh.add(
         BoundaryCondition(
             cantilever_set["start"],
             {
@@ -332,7 +339,7 @@ def test_four_c_simulation_beam_and_solid_tube(
             bc_type=mpy.bc.dirichlet,
         )
     )
-    input_file.add(
+    mesh.add(
         BoundaryCondition(
             cantilever_set["end"],
             {
@@ -356,7 +363,10 @@ def test_four_c_simulation_beam_and_solid_tube(
 
     # Call get_unique_geometry_sets to check that this does not affect the
     # mesh creation.
-    input_file.get_unique_geometry_sets(link_to_nodes="all_nodes")
+    mesh.get_unique_geometry_sets(link_to_nodes="all_nodes")
+
+    # Add mesh to input file
+    input_file.add(mesh)
 
     # Check the created input file
     assert_results_equal(
@@ -565,9 +575,12 @@ def test_four_c_simulation_rotated_beam_axis(
         and couples them with a coupling.
     """
 
-    # Create input file.
+    # Create input file
     input_file = InputFile()
 
+    # Create mesh
+    mesh = Mesh()
+
     # Set header
     set_header_static(input_file, time_step=0.05, n_steps=20)
 
@@ -585,17 +598,17 @@ def test_four_c_simulation_rotated_beam_axis(
 
     # Create mesh.
     for i in range(3):
-        mesh = Mesh()
+        mesh_line = Mesh()
 
         # Create the first line.
         set_1 = create_beam_mesh_line(
-            mesh, Beam3rHerm2Line3, mat, [0, 0, 0], 1.0 * direction, n_el=3
+            mesh_line, Beam3rHerm2Line3, mat, [0, 0, 0], 1.0 * direction, n_el=3
         )
 
         if not i == 0:
             # In the second case rotate the line, so the triads do not
             # match any more.
-            mesh.rotate(Rotation(direction, alpha))
+            mesh_line.rotate(Rotation(direction, alpha))
 
         if i == 2:
             # The third line is with couplings.
@@ -605,7 +618,7 @@ def test_four_c_simulation_rotated_beam_axis(
 
         # Add the second line.
         set_2 = create_beam_mesh_line(
-            mesh,
+            mesh_line,
             Beam3rHerm2Line3,
             mat,
             1.0 * direction,
@@ -615,7 +628,7 @@ def test_four_c_simulation_rotated_beam_axis(
         )
 
         # Add boundary conditions.
-        mesh.add(
+        mesh_line.add(
             BoundaryCondition(
                 set_1["start"],
                 {
@@ -627,7 +640,7 @@ def test_four_c_simulation_rotated_beam_axis(
                 bc_type=mpy.bc.dirichlet,
             )
         )
-        mesh.add(
+        mesh_line.add(
             BoundaryCondition(
                 set_2["end"],
                 {
@@ -642,19 +655,22 @@ def test_four_c_simulation_rotated_beam_axis(
 
         if i == 2:
             # In the third case add a coupling.
-            mesh.couple_nodes()
+            mesh_line.couple_nodes()
 
         # Add the mesh to the input file.
-        input_file.add(mesh)
+        mesh.add(mesh_line)
 
         # Each time move the whole mesh.
-        input_file.translate([1, 0, 0])
+        mesh.translate([1, 0, 0])
 
     # Add result checks.
     displacements = [[1.5015284845, 0.35139255451, -1.0126517891]] * 3
     nodes = [13, 26, 40]
     add_result_description(input_file, displacements, nodes)
 
+    # Add the mesh to the input file
+    input_file.add(mesh)
+
     # Check the created input file
     assert_results_equal(get_corresponding_reference_file_path(), input_file)
 
@@ -998,11 +1014,13 @@ def create_and_run_cantilever(n_el, *, n_proc=1):
         input_file = InputFile()
         set_header_static(input_file, time_step=0.25, n_steps=4)
         set_runtime_output(input_file, output_energy=True)
+
+        mesh = Mesh()
         mat = MaterialReissner(radius=0.1, youngs_modulus=10000.0)
         beam_set = create_beam_mesh_line(
-            input_file, Beam3rHerm2Line3, mat, [0, 0, 0], [1, 0, 0], n_el=n_el
+            mesh, Beam3rHerm2Line3, mat, [0, 0, 0], [1, 0, 0], n_el=n_el
         )
-        input_file.add(
+        mesh.add(
             BoundaryCondition(
                 beam_set["start"],
                 {
@@ -1019,7 +1037,7 @@ def create_and_run_cantilever(n_el, *, n_proc=1):
                 {"COMPONENT": 0, "SYMBOLIC_FUNCTION_OF_SPACE_TIME": "t"},
             ]
         )
-        input_file.add(
+        mesh.add(
             fun,
             BoundaryCondition(
                 beam_set["end"],
@@ -1032,6 +1050,9 @@ def create_and_run_cantilever(n_el, *, n_proc=1):
                 bc_type=mpy.bc.dirichlet,
             ),
         )
+
+        input_file.add(mesh)
+
         output_name = f"cantilever_convergence_{n_el}"
         run_four_c_test(tmp_path, output_name, input_file, n_proc=n_proc)
         testing_dir = tmp_path / output_name
@@ -1078,12 +1099,12 @@ def test_four_c_simulation_beam_to_beam_contact_example(
     h = 0.25
 
     # Set up mesh and material.
-    beam_to_beam_contact_simulation = InputFile()
+    mesh = Mesh()
     mat = MaterialReissner(radius=0.1, youngs_modulus=1)
 
     # Create a beam in x-axis.
     beam_x = create_beam_mesh_line(
-        beam_to_beam_contact_simulation,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [-l_beam / 2, 0, 0],
@@ -1093,7 +1114,7 @@ def test_four_c_simulation_beam_to_beam_contact_example(
 
     # Apply Dirichlet condition to start and end nodes of beam 1:
     for set_name in ["start", "end"]:
-        beam_to_beam_contact_simulation.add(
+        mesh.add(
             BoundaryCondition(
                 beam_x[set_name],
                 {
@@ -1108,7 +1129,7 @@ def test_four_c_simulation_beam_to_beam_contact_example(
 
     # Create a second beam in y-axis.
     beam_y = create_beam_mesh_line(
-        beam_to_beam_contact_simulation,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [0, -l_beam / 2, h],
@@ -1122,11 +1143,11 @@ def test_four_c_simulation_beam_to_beam_contact_example(
     # Create a linear interpolation function with the displacement.
     fun = create_linear_interpolation_function(t, disp_values)
 
-    beam_to_beam_contact_simulation.add(fun)
+    mesh.add(fun)
 
     # Apply Dirichlet conditions at starting and end node to displace the beam endings.
     for set_name in ["start", "end"]:
-        beam_to_beam_contact_simulation.add(
+        mesh.add(
             BoundaryCondition(
                 beam_y[set_name],
                 {
@@ -1141,15 +1162,21 @@ def test_four_c_simulation_beam_to_beam_contact_example(
 
     # Create a beam to beam contact boundary condition factory.
     add_beam_interaction_condition(
-        beam_to_beam_contact_simulation,
+        mesh,
         beam_x["line"],
         beam_y["line"],
         mpy.bc.beam_to_beam_contact,
     )
 
+    # Create the input file
+    input_file = InputFile()
+
+    # Add the mesh to the input file.
+    input_file.add(mesh)
+
     # Add the standard,static header.
     set_header_static(
-        beam_to_beam_contact_simulation,
+        input_file,
         time_step=0.05,
         n_steps=24,
         total_time=1.2,
@@ -1160,7 +1187,7 @@ def test_four_c_simulation_beam_to_beam_contact_example(
 
     # Set the parameters for beam to beam contact.
     set_beam_contact_section(
-        beam_to_beam_contact_simulation,
+        input_file,
         btb_penalty=50,
         penalty_regularization_g0=r_beam * 0.02,
         binning_parameters={
@@ -1170,29 +1197,21 @@ def test_four_c_simulation_beam_to_beam_contact_example(
     )
 
     # Add normal runtime output.
-    set_runtime_output(beam_to_beam_contact_simulation)
+    set_runtime_output(input_file)
 
     # Add special runtime output for beam interaction.
-    set_beam_contact_runtime_output(
-        beam_to_beam_contact_simulation, every_iteration=False
-    )
+    set_beam_contact_runtime_output(input_file, every_iteration=False)
 
     # Compare with the reference solution.
-    assert_results_equal(
-        get_corresponding_reference_file_path(), beam_to_beam_contact_simulation
-    )
+    assert_results_equal(get_corresponding_reference_file_path(), input_file)
 
     displacements = [[1.11158519615313324e-03, 0, -1.48443346935174636e-01]]
     nodes = [13]
-    add_result_description(
-        beam_to_beam_contact_simulation, displacements, nodes, tol=1e-8
-    )
+    add_result_description(input_file, displacements, nodes, tol=1e-8)
 
     # Check if we still have to actually run 4C.
     if not enforce_four_c:
         return
 
     initial_run_name = "beam_to_beam_contact_simulation"
-    run_four_c_test(
-        tmp_path, initial_run_name, beam_to_beam_contact_simulation, n_proc=1
-    )
+    run_four_c_test(tmp_path, initial_run_name, input_file, n_proc=1)
diff --git a/tests/test_mesh_creation_functions.py b/tests/test_mesh_creation_functions.py
index d398d51d..01001607 100644
--- a/tests/test_mesh_creation_functions.py
+++ b/tests/test_mesh_creation_functions.py
@@ -34,7 +34,6 @@
 from meshpy.core.node import NodeCosserat
 from meshpy.core.rotation import Rotation
 from meshpy.four_c.element_beam import Beam3eb, Beam3rHerm2Line3
-from meshpy.four_c.input_file import InputFile
 from meshpy.four_c.material import MaterialEulerBernoulli, MaterialReissner
 from meshpy.mesh_creation_functions.beam_basic_geometry import (
     create_beam_mesh_arc_at_node,
@@ -135,11 +134,11 @@ def test_mesh_creation_functions_arc_segment_via_axis(
     reference file."""
 
     # Create mesh
-    input_file = InputFile()
+    mesh = Mesh()
     mat = MaterialReissner()
     radius = 2.0
     beam_set = create_beam_mesh_arc_segment_via_axis(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [0, 0, 1],
@@ -148,10 +147,10 @@ def test_mesh_creation_functions_arc_segment_via_axis(
         1.0,
         n_el=3,
     )
-    input_file.add(beam_set)
+    mesh.add(beam_set)
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_arc_segment_start_end_node(
@@ -170,14 +169,14 @@ def test_mesh_creation_functions_arc_segment_start_end_node(
     def create_beam(*, start_node=None, end_node=None):
         """This is the base function we use to generate the beam in this test
         case."""
-        input_file = InputFile()
+        mesh = Mesh()
         mat = MaterialReissner()
         if start_node is not None:
-            input_file.add(start_node)
+            mesh.add(start_node)
         if end_node is not None:
-            input_file.add(end_node)
+            mesh.add(end_node)
         create_beam_mesh_arc_segment_via_axis(
-            input_file,
+            mesh,
             Beam3rHerm2Line3,
             mat,
             [0, 0, 1],
@@ -188,13 +187,13 @@ def create_beam(*, start_node=None, end_node=None):
             start_node=start_node,
             end_node=end_node,
         )
-        return input_file
+        return mesh
 
     # This should work as expected, as all the values match.
     start_node = NodeCosserat(start_node_pos, start_node_rot)
     end_node = NodeCosserat(end_node_pos, end_node_rot)
-    input_file = create_beam(start_node=start_node, end_node=end_node)
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    mesh = create_beam(start_node=start_node, end_node=end_node)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
     # Create with start node where the position does not match.
     with pytest.raises(
@@ -235,15 +234,15 @@ def test_mesh_creation_functions_arc_segment_via_rotation(
     """Create a circular segment via the rotation method and compare it with
     the reference file."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and function.
     mat = MaterialReissner(youngs_modulus=2.07e2, radius=0.1, shear_correction=1.1)
 
     # Create mesh.
     beam_set = create_beam_mesh_arc_segment_via_rotation(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [3, 6, 9.2],
@@ -254,10 +253,10 @@ def test_mesh_creation_functions_arc_segment_via_rotation(
     )
 
     # Add boundary conditions.
-    input_file.add(beam_set)
+    mesh.add(beam_set)
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_arc_segment_2d(
@@ -265,15 +264,15 @@ def test_mesh_creation_functions_arc_segment_2d(
 ):
     """Create a circular segments in 2D."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and function.
     mat = MaterialReissner(radius=0.1)
 
     # Create mesh.
     beam_set_1 = create_beam_mesh_arc_segment_2d(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [1.0, 2.0, 0.0],
@@ -283,7 +282,7 @@ def test_mesh_creation_functions_arc_segment_2d(
         n_el=5,
     )
     beam_set_2 = create_beam_mesh_arc_segment_2d(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [1.0, 2.0, 0.0] - 2.0 * 0.5 * np.array([1, np.sqrt(3), 0]),
@@ -291,14 +290,14 @@ def test_mesh_creation_functions_arc_segment_2d(
         np.pi / 3.0,
         -np.pi,
         n_el=3,
-        start_node=input_file.nodes[-1],
+        start_node=mesh.nodes[-1],
     )
 
     # Add geometry sets
-    input_file.add(beam_set_1, beam_set_2)
+    mesh.add(beam_set_1, beam_set_2)
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_node_positions_of_elements_option(
@@ -307,8 +306,8 @@ def test_mesh_creation_functions_node_positions_of_elements_option(
     """Creates a line, a circular segments in 2D and a helix by setting the
     node_positions_of_elements."""
 
-    # Create a mesh.
-    mesh = InputFile()
+    # Create a mesh
+    mesh = Mesh()
 
     # Create and add material to mesh.
     material = MaterialReissner()
@@ -371,15 +370,15 @@ def test_mesh_creation_functions_stent(
 ):
     """Test the stent creation function."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and function.
     mat = MaterialReissner()
 
     # Create mesh.
     create_beam_mesh_stent(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         0.11,
@@ -394,9 +393,7 @@ def test_mesh_creation_functions_stent(
     )
 
     # Check the output.
-    assert_results_equal(
-        get_corresponding_reference_file_path(), input_file, rtol=1e-10
-    )
+    assert_results_equal(get_corresponding_reference_file_path(), mesh, rtol=1e-10)
 
 
 def test_mesh_creation_functions_fibers_in_rectangle(
@@ -404,30 +401,30 @@ def test_mesh_creation_functions_fibers_in_rectangle(
 ):
     """Test the create_fibers_in_rectangle function."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Create mesh.
     mat = MaterialEulerBernoulli()
-    create_fibers_in_rectangle(input_file, Beam3eb, mat, 4, 1, 45, 0.45, 0.35)
-    input_file.translate([0, 0, 1])
-    create_fibers_in_rectangle(input_file, Beam3eb, mat, 4, 1, 0, 0.45, 0.35)
-    input_file.translate([0, 0, 1])
-    create_fibers_in_rectangle(input_file, Beam3eb, mat, 4, 1, 90, 0.45, 0.35)
-    input_file.translate([0, 0, 1])
-    create_fibers_in_rectangle(input_file, Beam3eb, mat, 4, 1, -90, 0.45, 0.35)
-    input_file.translate([0, 0, 1])
-    create_fibers_in_rectangle(input_file, Beam3eb, mat, 4, 1, 235, 0.45, 0.35)
-    input_file.translate([0, 0, 1])
+    create_fibers_in_rectangle(mesh, Beam3eb, mat, 4, 1, 45, 0.45, 0.35)
+    mesh.translate([0, 0, 1])
+    create_fibers_in_rectangle(mesh, Beam3eb, mat, 4, 1, 0, 0.45, 0.35)
+    mesh.translate([0, 0, 1])
+    create_fibers_in_rectangle(mesh, Beam3eb, mat, 4, 1, 90, 0.45, 0.35)
+    mesh.translate([0, 0, 1])
+    create_fibers_in_rectangle(mesh, Beam3eb, mat, 4, 1, -90, 0.45, 0.35)
+    mesh.translate([0, 0, 1])
+    create_fibers_in_rectangle(mesh, Beam3eb, mat, 4, 1, 235, 0.45, 0.35)
+    mesh.translate([0, 0, 1])
     create_fibers_in_rectangle(
-        input_file, Beam3eb, mat, 1, 4, 30, 0.45, 5, fiber_element_length_min=0.2
+        mesh, Beam3eb, mat, 1, 4, 30, 0.45, 5, fiber_element_length_min=0.2
     )
-    input_file.translate([0, 0, 1])
-    create_fibers_in_rectangle(input_file, Beam3eb, mat, 4, 1, 30, 0.45, 0.9)
-    input_file.translate([0, 0, 1])
+    mesh.translate([0, 0, 1])
+    create_fibers_in_rectangle(mesh, Beam3eb, mat, 4, 1, 30, 0.45, 0.9)
+    mesh.translate([0, 0, 1])
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_fibers_in_rectangle_reference_point(
@@ -436,17 +433,17 @@ def test_mesh_creation_functions_fibers_in_rectangle_reference_point(
     """Test the create_fibers_in_rectangle function with using the
     reference_point option."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Create mesh.
     mat = MaterialEulerBernoulli()
-    create_fibers_in_rectangle(input_file, Beam3eb, mat, 4, 1, 45, 0.45, 0.35)
+    create_fibers_in_rectangle(mesh, Beam3eb, mat, 4, 1, 45, 0.45, 0.35)
     reference_point = 0.5 * np.array([4.0, 1.0]) + 0.1 * np.array(
         [-1.0, 1.0]
     ) / np.sqrt(2.0)
     create_fibers_in_rectangle(
-        input_file,
+        mesh,
         Beam3eb,
         mat,
         4,
@@ -458,7 +455,7 @@ def test_mesh_creation_functions_fibers_in_rectangle_reference_point(
     )
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_fibers_in_rectangle_return_set(
@@ -466,18 +463,16 @@ def test_mesh_creation_functions_fibers_in_rectangle_return_set(
 ):
     """Test the set returned by the create_fibers_in_rectangle function."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Create mesh.
     mat = MaterialEulerBernoulli()
-    beam_set = create_fibers_in_rectangle(
-        input_file, Beam3eb, mat, 4, 1, 45, 0.45, 0.35
-    )
-    input_file.add(beam_set)
+    beam_set = create_fibers_in_rectangle(mesh, Beam3eb, mat, 4, 1, 45, 0.45, 0.35)
+    mesh.add(beam_set)
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_wire(
@@ -485,8 +480,8 @@ def test_mesh_creation_functions_wire(
 ):
     """Test the create_wire_fibers function."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Create two wires with different parameters.
     mat = MaterialEulerBernoulli(radius=0.05)
@@ -495,10 +490,10 @@ def test_mesh_creation_functions_wire(
     mesh_2 = Mesh()
     set_2 = create_wire_fibers(mesh_2, Beam3eb, mat, 3.0, layers=2, n_el=2, radius=0.1)
     mesh_2.translate([0.0, 1.5, 0.0])
-    input_file.add(mesh_1, mesh_2, set_1, set_2)
+    mesh.add(mesh_1, mesh_2, set_1, set_2)
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_nurbs(
@@ -515,10 +510,7 @@ def test_mesh_creation_functions_nurbs(
     )
     assert np.isclose(3.140204411551537, length, rtol=mpy.eps_pos, atol=0.0)
 
-    # Check the output.
-    input_file = InputFile()
-    input_file.add(mesh)
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_nurbs_unit():
@@ -593,10 +585,7 @@ def test_mesh_creation_functions_node_continuation(
         mesh, Beam3rHerm2Line3, mat, beam_set["end"], 2.3, n_el=3
     )
 
-    # Check the geometry
-    input_file = InputFile()
-    input_file.add(mesh)
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_node_continuation_accumulated():
@@ -652,7 +641,7 @@ def test_mesh_creation_functions_element_length_option(
     """Test that the element length can be specified in the beam creation
     functions."""
 
-    input_file = InputFile()
+    mesh = Mesh()
     mat = MaterialReissner(radius=0.1)
 
     l_el = 1.5
@@ -706,10 +695,10 @@ def test_mesh_creation_functions_element_length_option(
     )
 
     # Check the output
-    input_file.add(mesh_line, mesh_line_long, mesh_arc, mesh_curve)
+    mesh.add(mesh_line, mesh_line_long, mesh_arc, mesh_curve)
     assert_results_equal(
         get_corresponding_reference_file_path(),
-        input_file,
+        mesh,
         rtol=1e-10,
     )
 
@@ -883,6 +872,7 @@ def create_beam(mesh, **kwargs):
                 node_positions_of_elements=node_positions_of_elements,
                 **kwargs,
             )
+
     elif basic_creation_function == "arc":
         length = np.pi * 0.5
         start_pos = [0, 1, 0]
@@ -1134,8 +1124,8 @@ def test_mesh_creation_functions_curve_3d_helix(
     transformed so the arc length along the beam is not proportional to the
     parameter."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and functions.
     mat = MaterialReissner()
@@ -1148,9 +1138,9 @@ def test_mesh_creation_functions_curve_3d_helix(
     helix = create_helix_function(R, tz, transformation_factor=2.0, number_of_turns=n)
 
     helix_set = create_beam_mesh_curve(
-        input_file, Beam3rHerm2Line3, mat, helix, [0.0, 2.0 * np.pi * n], n_el=n_el
+        mesh, Beam3rHerm2Line3, mat, helix, [0.0, 2.0 * np.pi * n], n_el=n_el
     )
-    input_file.add(helix_set)
+    mesh.add(helix_set)
 
     # Compare the coordinates with the ones from Mathematica.
     coordinates_mathematica = np.loadtxt(
@@ -1161,21 +1151,21 @@ def test_mesh_creation_functions_curve_3d_helix(
     )
     assert np.allclose(
         coordinates_mathematica,
-        get_nodal_coordinates(input_file.nodes),
+        get_nodal_coordinates(mesh.nodes),
         rtol=mpy.eps_pos,
         atol=1e-14,
     )
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_curve_3d_helix_length(assert_results_equal):
     """Create a helix from a parametric curve where and check that the correct
     length is returned."""
 
-    input_file_1 = InputFile()
-    input_file_2 = InputFile()
+    mesh_1 = Mesh()
+    mesh_2 = Mesh()
     mat = MaterialReissner()
 
     # Get the helix curve function
@@ -1188,19 +1178,19 @@ def test_mesh_creation_functions_curve_3d_helix_length(assert_results_equal):
     args = [Beam3rHerm2Line3, mat, helix, [0.0, 2.0 * np.pi * n]]
     kwargs = {"n_el": n_el}
 
-    helix_set_1 = create_beam_mesh_curve(input_file_1, *args, **kwargs)
-    input_file_1.add(helix_set_1)
+    helix_set_1 = create_beam_mesh_curve(mesh_1, *args, **kwargs)
+    mesh_1.add(helix_set_1)
 
     helix_set_2, length = create_beam_mesh_curve(
-        input_file_2, *args, output_length=True, **kwargs
+        mesh_2, *args, output_length=True, **kwargs
     )
-    input_file_2.add(helix_set_2)
+    mesh_2.add(helix_set_2)
 
     # Check the computed length
     assert np.isclose(length, 13.18763323790246, rtol=1e-12, atol=0.0)
 
     # Check that both meshes are equal
-    assert_results_equal(input_file_1, input_file_2)
+    assert_results_equal(mesh_1, mesh_2)
 
 
 def test_mesh_creation_functions_curve_2d_sin(
@@ -1208,8 +1198,8 @@ def test_mesh_creation_functions_curve_2d_sin(
 ):
     """Create a sin from a parametric curve."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and functions.
     mat = MaterialReissner()
@@ -1222,9 +1212,9 @@ def sin(t):
         return npAD.array([t, npAD.sin(t)])
 
     sin_set = create_beam_mesh_curve(
-        input_file, Beam3rHerm2Line3, mat, sin, [0.0, 2.0 * np.pi], n_el=n_el
+        mesh, Beam3rHerm2Line3, mat, sin, [0.0, 2.0 * np.pi], n_el=n_el
     )
-    input_file.add(sin_set)
+    mesh.add(sin_set)
 
     # Compare the coordinates with the ones from Mathematica.
     coordinates_mathematica = np.loadtxt(
@@ -1235,13 +1225,13 @@ def sin(t):
     )
     assert np.allclose(
         coordinates_mathematica,
-        get_nodal_coordinates(input_file.nodes),
+        get_nodal_coordinates(mesh.nodes),
         rtol=mpy.eps_pos,
         atol=1e-14,
     )
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_curve_3d_curve_rotation(
@@ -1249,8 +1239,8 @@ def test_mesh_creation_functions_curve_3d_curve_rotation(
 ):
     """Create a line from a parametric curve and prescribe the rotation."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and functions.
     mat = MaterialReissner()
@@ -1272,7 +1262,7 @@ def rotation(t):
         return R2 * R1
 
     sin_set = create_beam_mesh_curve(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         curve,
@@ -1280,11 +1270,11 @@ def rotation(t):
         n_el=n_el,
         function_rotation=rotation,
     )
-    input_file.add(sin_set)
+    mesh.add(sin_set)
 
     # extend test case with different meshing strategy
     create_beam_mesh_curve(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         curve,
@@ -1294,7 +1284,7 @@ def rotation(t):
     )
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_curve_3d_line(
@@ -1307,8 +1297,8 @@ def test_mesh_creation_functions_curve_3d_line(
     vectors.
     """
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and function.
     mat = MaterialReissner(youngs_modulus=2.07e2, radius=0.1, shear_correction=1.1)
@@ -1322,16 +1312,16 @@ def line(t):
 
     # Create mesh.
     set_1 = create_beam_mesh_curve(
-        input_file, Beam3rHerm2Line3, mat, line, [0.0, 5.0], n_el=3
+        mesh, Beam3rHerm2Line3, mat, line, [0.0, 5.0], n_el=3
     )
-    input_file.translate([0, 1, 0])
+    mesh.translate([0, 1, 0])
     set_2 = create_beam_mesh_curve(
-        input_file, Beam3rHerm2Line3, mat, line, [5.0, 0.0], n_el=3
+        mesh, Beam3rHerm2Line3, mat, line, [5.0, 0.0], n_el=3
     )
-    input_file.add(set_1, set_2)
+    mesh.add(set_1, set_2)
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_helix_no_rotation(
@@ -1340,20 +1330,20 @@ def test_mesh_creation_functions_helix_no_rotation(
     """Create a helix and compare it with the reference file."""
 
     ## Helix angle and height helix combination
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and function.
     mat = MaterialReissner(youngs_modulus=1e5, radius=0.5, shear_correction=1.0)
 
     # Add simple line to verify that the helix creation does not alter additional meshes
     create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, mat, [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]
+        mesh, Beam3rHerm2Line3, mat, [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]
     )
 
     # Create helix.
     helix_set = create_beam_mesh_helix(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [0.0, 0.0, 1.0],
@@ -1363,26 +1353,26 @@ def test_mesh_creation_functions_helix_no_rotation(
         height_helix=10.0,
         l_el=5.0,
     )
-    input_file.add(helix_set)
+    mesh.add(helix_set)
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
     ## Helix angle and turns
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and function.
     mat = MaterialReissner(youngs_modulus=1e5, radius=0.5, shear_correction=1.0)
 
     # Add simple line to verify that the helix creation does not alter additional meshes
     create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, mat, [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]
+        mesh, Beam3rHerm2Line3, mat, [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]
     )
 
     # Create helix.
     helix_set = create_beam_mesh_helix(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [0.0, 0.0, 1.0],
@@ -1392,25 +1382,25 @@ def test_mesh_creation_functions_helix_no_rotation(
         turns=2.5 / np.pi,
         l_el=5.0,
     )
-    input_file.add(helix_set)
+    mesh.add(helix_set)
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and function.
     mat = MaterialReissner(youngs_modulus=1e5, radius=0.5, shear_correction=1.0)
 
     # Add simple line to verify that the helix creation does not alter additional meshes
     create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, mat, [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]
+        mesh, Beam3rHerm2Line3, mat, [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]
     )
 
     # Create helix.
     helix_set = create_beam_mesh_helix(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [0.0, 0.0, 1.0],
@@ -1420,10 +1410,10 @@ def test_mesh_creation_functions_helix_no_rotation(
         turns=2.5 / np.pi,
         l_el=5.0,
     )
-    input_file.add(helix_set)
+    mesh.add(helix_set)
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_helix_rotation_offset(
@@ -1431,20 +1421,20 @@ def test_mesh_creation_functions_helix_rotation_offset(
 ):
     """Create a helix and compare it with the reference file."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and function.
     mat = MaterialReissner(youngs_modulus=1e5, radius=0.5, shear_correction=1.0)
 
     # Add simple line to verify that the helix creation does not alter additional meshes
     create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, mat, [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]
+        mesh, Beam3rHerm2Line3, mat, [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]
     )
 
     # Create helix.
     helix_set = create_beam_mesh_helix(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [1.0, 1.0, 1.0],
@@ -1454,10 +1444,10 @@ def test_mesh_creation_functions_helix_rotation_offset(
         height_helix=10.0,
         l_el=5.0,
     )
-    input_file.add(helix_set)
+    mesh.add(helix_set)
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_helix_radius_zero(
@@ -1465,20 +1455,20 @@ def test_mesh_creation_functions_helix_radius_zero(
 ):
     """Create a helix and compare it with the reference file."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and function.
     mat = MaterialReissner(youngs_modulus=1e5, radius=0.5, shear_correction=1.0)
 
     # Add simple line to verify that the helix creation does not alter additional meshes
     create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, mat, [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]
+        mesh, Beam3rHerm2Line3, mat, [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]
     )
 
     # Create helix.
     helix_set = create_beam_mesh_helix(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [1.0, 1.0, 1.0],
@@ -1489,10 +1479,10 @@ def test_mesh_creation_functions_helix_radius_zero(
         n_el=4,
         warning_straight_line=False,
     )
-    input_file.add(helix_set)
+    mesh.add(helix_set)
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_mesh_creation_functions_helix_helix_angle_right_angle(
@@ -1500,20 +1490,20 @@ def test_mesh_creation_functions_helix_helix_angle_right_angle(
 ):
     """Create a helix and compare it with the reference file."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material and function.
     mat = MaterialReissner(youngs_modulus=1e5, radius=0.5, shear_correction=1.0)
 
     # Add simple line to verify that the helix creation does not alter additional meshes
     create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, mat, [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]
+        mesh, Beam3rHerm2Line3, mat, [0.0, 0.0, 0.0], [-1.0, -1.0, -1.0]
     )
 
     # Create helix.
     helix_set = create_beam_mesh_helix(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         mat,
         [1.0, 1.0, 1.0],
@@ -1524,7 +1514,7 @@ def test_mesh_creation_functions_helix_helix_angle_right_angle(
         l_el=5.0,
         warning_straight_line=False,
     )
-    input_file.add(helix_set)
+    mesh.add(helix_set)
 
     # Check the output.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
diff --git a/tests/test_meshpy.py b/tests/test_meshpy.py
index 59b192e1..50508398 100644
--- a/tests/test_meshpy.py
+++ b/tests/test_meshpy.py
@@ -112,10 +112,10 @@ def test_meshpy_rotations(assert_results_equal):
     """Check if the Mesh function rotation gives the same results as rotating
     each node it self."""
 
-    mesh_1 = InputFile()
+    mesh_1 = Mesh()
     create_test_mesh(mesh_1)
 
-    mesh_2 = InputFile()
+    mesh_2 = Mesh()
     create_test_mesh(mesh_2)
 
     # Set the seed for the pseudo random numbers
@@ -139,10 +139,10 @@ def test_meshpy_mesh_rotations_individual(assert_results_equal):
     """Check if the Mesh function rotation gives the same results as rotating
     each node it self, when an array is passed with different rotations."""
 
-    mesh_1 = InputFile()
+    mesh_1 = Mesh()
     create_test_mesh(mesh_1)
 
-    mesh_2 = InputFile()
+    mesh_2 = Mesh()
     create_test_mesh(mesh_2)
 
     # Set the seed for the pseudo random numbers
@@ -175,8 +175,8 @@ def test_meshpy_mesh_reflection(origin, flip, assert_results_equal):
     rot_1 = Rotation([0, 1, 1], np.pi / 6)
     rot_2 = Rotation([1, 2.455, -1.2324], 1.2342352)
 
-    mesh_ref = InputFile()
-    mesh = InputFile()
+    mesh_ref = Mesh()
+    mesh = Mesh()
     mat = MaterialReissner(radius=0.1)
 
     # Create the reference mesh.
@@ -237,39 +237,6 @@ def test_meshpy_mesh_reflection(origin, flip, assert_results_equal):
     assert_results_equal(mesh_ref, mesh)
 
 
-@pytest.mark.parametrize("full_import", [[True, "full"], [False, None]])
-def test_meshpy_comments_in_solid(
-    get_bc_data,
-    assert_results_equal,
-    get_corresponding_reference_file_path,
-    full_import,
-):
-    """Check if comments in the solid file are handled correctly if they are
-    inside a mesh section."""
-
-    # Convert the solid mesh to meshpy objects.
-    mpy.import_mesh_full = full_import[0]
-    solid_file = get_corresponding_reference_file_path(additional_identifier="initial")
-    mesh = InputFile(yaml_file=solid_file)
-
-    # Add one element with BCs.
-    mat = MaterialReissner()
-    sets = create_beam_mesh_line(mesh, Beam3rHerm2Line3, mat, [0, 0, 0], [1, 2, 3])
-    mesh.add(
-        BoundaryCondition(
-            sets["start"], get_bc_data(identifier=1), bc_type=mpy.bc.dirichlet
-        )
-    )
-    mesh.add(
-        BoundaryCondition(
-            sets["end"], get_bc_data(identifier=2), bc_type=mpy.bc.neumann
-        )
-    )
-
-    # Compare the output of the mesh.
-    assert_results_equal(get_corresponding_reference_file_path(), mesh)
-
-
 def test_meshpy_mesh_transformations_with_solid(
     assert_results_equal,
     get_corresponding_reference_file_path,
@@ -277,6 +244,8 @@ def test_meshpy_mesh_transformations_with_solid(
     """Test the different mesh transformation methods in combination with solid
     elements."""
 
+    # TODO use pytest fixtures for the different setups
+
     def base_test_mesh_translations(*, import_full=False, radius=None, reflect=True):
         """Create the line and wrap it with passing radius to the wrap
         function."""
@@ -285,7 +254,15 @@ def base_test_mesh_translations(*, import_full=False, radius=None, reflect=True)
         mpy.import_mesh_full = import_full
 
         # Create the mesh.
-        mesh = InputFile(
+
+        # TODO rework this test once the input file is refactored, i.e.
+        # input_file, mesh = InputFile(yaml_file=...)
+        # ... add different geometry sets to the mesh
+        # input_file.add(mesh)
+        # this way it is tested exhaustively rather than accessing the mesh
+        # in the input file during the beam mesh creation
+
+        input_file = InputFile(
             yaml_file=get_corresponding_reference_file_path(
                 reference_file_base_name="4C_input_solid_cuboid"
             )
@@ -294,7 +271,7 @@ def base_test_mesh_translations(*, import_full=False, radius=None, reflect=True)
 
         # Create the line.
         create_beam_mesh_line(
-            mesh,
+            input_file.mesh,
             Beam3rHerm2Line3,
             mat,
             [0.2, 0, 0],
@@ -303,18 +280,18 @@ def base_test_mesh_translations(*, import_full=False, radius=None, reflect=True)
         )
 
         # Transform the mesh.
-        mesh.wrap_around_cylinder(radius=radius)
-        mesh.translate([1, 2, 3])
-        mesh.rotate(Rotation([1, 2, 3], np.pi * 17.0 / 27.0))
+        input_file.mesh.wrap_around_cylinder(radius=radius)
+        input_file.mesh.translate([1, 2, 3])
+        input_file.mesh.rotate(Rotation([1, 2, 3], np.pi * 17.0 / 27.0))
         if reflect:
-            mesh.reflect([0.1, -2, 1])
+            input_file.mesh.reflect([0.1, -2, 1])
 
         # Check the output.
         assert_results_equal(
             get_corresponding_reference_file_path(
                 additional_identifier="full" if import_full else "yaml"
             ),
-            mesh,
+            input_file,
         )
 
     base_test_mesh_translations(import_full=False, radius=None)
@@ -353,6 +330,7 @@ def test_meshpy_fluid_element_section(
     create_beam_mesh_line(
         beam_mesh, Beam3eb, material, [0, -0.5, 0], [0, 0.2, 0], n_el=5
     )
+
     input_file.add(beam_mesh)
 
     # Check the output.
@@ -368,7 +346,7 @@ def test_meshpy_wrap_cylinder_not_on_same_plane(
     warnings.filterwarnings("ignore")
 
     # Create the mesh.
-    mesh = InputFile()
+    mesh = Mesh()
     mat = MaterialReissner(radius=0.05)
 
     # Create the line and bend it to a helix.
@@ -427,7 +405,7 @@ def test_meshpy_get_min_max_coordinates(get_corresponding_reference_file_path):
         yaml_file=get_corresponding_reference_file_path(
             reference_file_base_name="4C_input_solid_cuboid"
         )
-    )
+    ).mesh
     mat = MaterialReissner(radius=0.05)
     create_beam_mesh_line(mesh, Beam3rHerm2Line3, mat, [0, 0, 0], [2, 3, 4], n_el=10)
 
@@ -442,7 +420,7 @@ def test_meshpy_geometry_sets(
 ):
     """Test functionality of the GeometrySet objects."""
 
-    mesh = InputFile()
+    mesh = Mesh()
     for i in range(6):
         mesh.add(NodeCosserat([i, 2 * i, 3 * i], Rotation()))
 
@@ -470,16 +448,16 @@ def test_meshpy_unique_ordering_of_get_all_nodes_for_line_condition(
     function get_all_nodes is unique for line sets."""
 
     # set up a beam mesh with material
-    input_file = InputFile()
+    mesh = Mesh()
     mat = MaterialReissner()
     beam_set = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, mat, [0, 0, 0], [2, 0, 0], n_el=10
+        mesh, Beam3rHerm2Line3, mat, [0, 0, 0], [2, 0, 0], n_el=10
     )
 
     # apply different Dirichlet conditions to all nodes within this condition
     for i, node in enumerate(beam_set["line"].get_all_nodes()):
         # add different condition value for each node
-        input_file.add(
+        mesh.add(
             BoundaryCondition(
                 GeometrySet(node),
                 get_bc_data(identifier=node.coordinates[0]),
@@ -490,7 +468,7 @@ def test_meshpy_unique_ordering_of_get_all_nodes_for_line_condition(
     # Check the input file
     assert_results_equal(
         get_corresponding_reference_file_path(),
-        input_file,
+        mesh,
     )
 
 
@@ -500,14 +478,16 @@ def test_meshpy_reissner_beam(
     """Test that the input file for all types of Reissner beams is generated
     correctly."""
 
-    # Create input file.
+    # Create mesh
+    mesh = Mesh()
+
+    # Create material
     material = MaterialReissner(radius=0.1, youngs_modulus=1000, interaction_radius=2.0)
-    input_file = InputFile()
 
     # Create a beam arc with the different Reissner beam types.
     for i, beam_type in enumerate([Beam3rHerm2Line3, Beam3rLine2Line2]):
         create_beam_mesh_arc_segment_via_rotation(
-            input_file,
+            mesh,
             beam_type,
             material,
             [0.0, 0.0, i],
@@ -518,7 +498,7 @@ def test_meshpy_reissner_beam(
         )
 
     # Compare with the reference solution.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_meshpy_reissner_elasto_plastic(assert_results_equal):
@@ -569,8 +549,8 @@ def test_meshpy_kirchhoff_beam(
     """Test that the input file for all types of Kirchhoff beams is generated
     correctly."""
 
-    # Create input file.
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     with warnings.catch_warnings():
         # Ignore the warnings for the rotvec beams.
@@ -587,7 +567,7 @@ def test_meshpy_kirchhoff_beam(
 
                     # Create a beam.
                     set_1 = create_beam_mesh_line(
-                        input_file,
+                        mesh,
                         BeamObject,
                         material,
                         [0, 0, 0],
@@ -595,7 +575,7 @@ def test_meshpy_kirchhoff_beam(
                         n_el=2,
                     )
                     set_2 = create_beam_mesh_line(
-                        input_file,
+                        mesh,
                         BeamObject,
                         material,
                         [1, 0, 0],
@@ -605,7 +585,7 @@ def test_meshpy_kirchhoff_beam(
 
                     # Couple the nodes.
                     if rotvec:
-                        input_file.couple_nodes(
+                        mesh.couple_nodes(
                             nodes=[
                                 get_single_node(set_1["end"]),
                                 get_single_node(set_2["start"]),
@@ -613,10 +593,10 @@ def test_meshpy_kirchhoff_beam(
                         )
 
                     # Move the mesh away from the next created beam.
-                    input_file.translate([0, 0.5, 0])
+                    mesh.translate([0, 0.5, 0])
 
     # Compare with the reference solution.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_meshpy_kirchhoff_material(assert_results_equal):
@@ -700,10 +680,10 @@ def test_meshpy_euler_bernoulli(
     """Recreate the 4C test case beam3eb_static_endmoment_quartercircle.4C.yaml
     This tests the implementation for Euler Bernoulli beams."""
 
-    # Create the input file and add function and material.
-    input_file = InputFile()
+    # Create the mesh and add function and material.
+    mesh = Mesh()
     fun = Function([{"COMPONENT": 0, "SYMBOLIC_FUNCTION_OF_SPACE_TIME": "t"}])
-    input_file.add(fun)
+    mesh.add(fun)
     mat = MaterialEulerBernoulli(youngs_modulus=1.0, density=1.3e9)
 
     # Set the parameters that are also set in the test file.
@@ -711,12 +691,10 @@ def test_meshpy_euler_bernoulli(
     mat.mom2 = 1e-4
 
     # Create the beam.
-    beam_set = create_beam_mesh_line(
-        input_file, Beam3eb, mat, [-1, 0, 0], [1, 0, 0], n_el=16
-    )
+    beam_set = create_beam_mesh_line(mesh, Beam3eb, mat, [-1, 0, 0], [1, 0, 0], n_el=16)
 
     # Add boundary conditions.
-    input_file.add(
+    mesh.add(
         BoundaryCondition(
             beam_set["start"],
             {
@@ -728,7 +706,7 @@ def test_meshpy_euler_bernoulli(
             bc_type=mpy.bc.dirichlet,
         )
     )
-    input_file.add(
+    mesh.add(
         BoundaryCondition(
             beam_set["end"],
             {
@@ -742,20 +720,22 @@ def test_meshpy_euler_bernoulli(
     )
 
     # Compare with the reference solution.
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
     # Test consistency checks.
     rot = Rotation([1, 2, 3], 2.3434)
-    input_file.nodes[-1].rotation = rot
+    mesh.nodes[-1].rotation = rot
     with pytest.raises(
         ValueError,
         match="The two nodal rotations in Euler Bernoulli beams must be the same",
     ):
         # This raises an error because not all rotation in the beams are
         # the same.
+        input_file = InputFile()
+        input_file.add(mesh)
         input_file.get_dict_to_dump()
 
-    for node in input_file.nodes:
+    for node in mesh.nodes:
         node.rotation = rot
     with pytest.raises(
         ValueError,
@@ -763,6 +743,8 @@ def test_meshpy_euler_bernoulli(
     ):
         # This raises an error because the rotations do not match the
         # director between the nodes.
+        input_file = InputFile()
+        input_file.add(mesh)
         input_file.get_dict_to_dump()
 
 
@@ -787,8 +769,8 @@ def test_meshpy_close_beam(assert_results_equal, get_corresponding_reference_fil
 
     def create_mesh_manually(start_rotation):
         """Create the full circle manually."""
-        input_file = InputFile()
-        input_file.add(mat)
+        mesh = Mesh()
+        mesh.add(mat)
 
         # Add nodes.
         for i in range(4 * n_el):
@@ -797,64 +779,62 @@ def create_mesh_manually(start_rotation):
             node = NodeCosserat(r, basis)
             rotation = Rotation([0, 0, 1], 0.5 * i * np.pi / n_el)
             node.rotate(rotation, origin=[0, 0, 0])
-            input_file.nodes.append(node)
+            mesh.nodes.append(node)
 
         # Add elements.
         for i in range(2 * n_el):
             node_index = [2 * i, 2 * i + 1, 2 * i + 2]
             nodes = []
             for index in node_index:
-                if index == len(input_file.nodes):
-                    nodes.append(input_file.nodes[0])
+                if index == len(mesh.nodes):
+                    nodes.append(mesh.nodes[0])
                 else:
-                    nodes.append(input_file.nodes[index])
+                    nodes.append(mesh.nodes[index])
             element = Beam3rHerm2Line3(mat, nodes)
-            input_file.add(element)
+            mesh.add(element)
 
         # Add sets.
         geom_set = GeometryName()
-        geom_set["start"] = GeometrySet(input_file.nodes[0])
-        geom_set["end"] = GeometrySet(input_file.nodes[0])
-        geom_set["line"] = GeometrySet(input_file.elements)
-        input_file.add(geom_set)
-        return input_file
+        geom_set["start"] = GeometrySet(mesh.nodes[0])
+        geom_set["end"] = GeometrySet(mesh.nodes[0])
+        geom_set["line"] = GeometrySet(mesh.elements)
+        mesh.add(geom_set)
+        return mesh
 
     def one_full_circle_closed(function, argument_list, additional_rotation=None):
         """Create one full circle and connect it to itself."""
 
-        input_file = InputFile()
+        mesh = Mesh()
 
         if additional_rotation is not None:
             start_rotation = additional_rotation * Rotation([0, 0, 1], np.pi * 0.5)
-            input_file.add(NodeCosserat([R, 0, 0], start_rotation))
+            mesh.add(NodeCosserat([R, 0, 0], start_rotation))
             beam_sets = function(
-                input_file,
-                start_node=input_file.nodes[0],
+                mesh,
+                start_node=mesh.nodes[0],
                 close_beam=True,
                 **(argument_list),
             )
         else:
-            beam_sets = function(input_file, close_beam=True, **(argument_list))
-        input_file.add(beam_sets)
-        return input_file
+            beam_sets = function(mesh, close_beam=True, **(argument_list))
+        mesh.add(beam_sets)
+        return mesh
 
     def two_half_circles_closed(function, argument_list, additional_rotation=None):
         """Create two half circles and close them, by reusing the connecting
         nodes."""
 
-        input_file = InputFile()
+        mesh = Mesh()
 
         if additional_rotation is not None:
             start_rotation = additional_rotation * Rotation([0, 0, 1], np.pi * 0.5)
-            input_file.add(NodeCosserat([R, 0, 0], start_rotation))
-            set_1 = function(
-                input_file, start_node=input_file.nodes[0], **(argument_list[0])
-            )
+            mesh.add(NodeCosserat([R, 0, 0], start_rotation))
+            set_1 = function(mesh, start_node=mesh.nodes[0], **(argument_list[0]))
         else:
-            set_1 = function(input_file, **(argument_list[0]))
+            set_1 = function(mesh, **(argument_list[0]))
 
         set_2 = function(
-            input_file,
+            mesh,
             start_node=set_1["end"],
             end_node=set_1["start"],
             **(argument_list[1]),
@@ -865,9 +845,9 @@ def two_half_circles_closed(function, argument_list, additional_rotation=None):
         geom_set["start"] = GeometrySet(set_1["start"])
         geom_set["end"] = GeometrySet(set_2["end"])
         geom_set["line"] = GeometrySet([set_1["line"], set_2["line"]])
-        input_file.add(geom_set)
+        mesh.add(geom_set)
 
-        return input_file
+        return mesh
 
     def get_arguments_arc_segment(circle_type):
         """Return the arguments for the arc segment function."""
@@ -1004,9 +984,9 @@ def test_geometry_set_get_geometry_objects():
     """Test if the geometry set returns the objects(elements) in the correct
     order."""
 
-    # Initialize material and input file.
+    # Initialize material and mesh
     mat = MaterialReissner()
-    mesh = InputFile()
+    mesh = Mesh()
 
     # number of elements
     n_el = 5
@@ -1038,13 +1018,10 @@ def test_meshpy_replace_nodes_geometry_set(
     mat = MaterialReissner(radius=0.1, youngs_modulus=1)
     rot = Rotation([1, 2, 43], 213123)
 
-    def create_mesh():
-        """Create two empty meshes."""
-        return InputFile(), InputFile()
-
     # Create a beam with two elements. Once immediately and once as two
     # beams with couplings.
-    mesh_ref, mesh_couple = create_mesh()
+    mesh_ref = Mesh()
+    mesh_couple = Mesh()
 
     # Create a simple beam.
     create_beam_mesh_line(mesh_ref, Beam3rHerm2Line3, mat, [0, 0, 0], [2, 0, 0], n_el=2)
@@ -1107,7 +1084,8 @@ def create_mesh():
 
     # Create two overlapping beams. This is to test that the middle nodes
     # are not coupled.
-    mesh_ref, mesh_couple = create_mesh()
+    mesh_ref = Mesh()
+    mesh_couple = Mesh()
 
     # Create a simple beam.
     set_ref = create_beam_mesh_line(
@@ -1139,7 +1117,8 @@ def create_mesh():
 
     # Create a beam with two elements. Once immediately and once as two
     # beams with couplings.
-    mesh_ref, mesh_couple = create_mesh()
+    mesh_ref = Mesh()
+    mesh_couple = Mesh()
 
     # Create a simple beam.
     create_beam_mesh_line(mesh_ref, Beam3rHerm2Line3, mat, [0, 0, 0], [2, 0, 0], n_el=2)
@@ -1194,7 +1173,10 @@ def create_mesh():
 def create_beam_to_solid_conditions_model(get_corresponding_reference_file_path):
     """Create the input file for the beam-to-solid input conditions tests."""
 
-    # Create input file.
+    # TODO update this function to take the "full_import" argument
+    # Then return the input file and corresponding mesh.
+
+    # Create input file
     input_file = InputFile(
         yaml_file=get_corresponding_reference_file_path(
             reference_file_base_name="test_create_cubit_input_block"
@@ -1229,7 +1211,7 @@ def create_beam_to_solid_conditions_model(get_corresponding_reference_file_path)
     )
 
     # Add the beam to the solid mesh.
-    input_file.add(beam_mesh)
+    input_file.mesh.add(beam_mesh)
 
     return input_file
 
@@ -1265,7 +1247,6 @@ def test_meshpy_surface_to_surface_contact_import(
     """Test that surface-to-surface contact problems can be imported as
     expected."""
 
-    # Create input file.
     mpy.import_mesh_full = True
     input_file = InputFile(
         yaml_file=get_corresponding_reference_file_path(
@@ -1286,7 +1267,8 @@ def test_meshpy_nurbs_import(
     beam3r_herm2line3_static_beam_to_solid_volume_meshtying_nurbs27_mortar_penalty_line4
     """
 
-    # Create beam mesh and load solid file.
+    # Create mesh and load solid file.
+    mesh = Mesh()
     input_file = InputFile(
         yaml_file=get_corresponding_reference_file_path(
             additional_identifier="solid_mesh"
@@ -1324,18 +1306,19 @@ def test_meshpy_nurbs_import(
         },
         option_overwrite=True,
     )
+
     fun = Function([{"COMPONENT": 0, "SYMBOLIC_FUNCTION_OF_SPACE_TIME": "t"}])
-    input_file.add(fun)
+    mesh.add(fun)
 
     # Create the beam material.
     material = MaterialReissner(youngs_modulus=1000, radius=0.05)
 
     # Create the beams.
     set_1 = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, material, [0, 0, 0.95], [1, 0, 0.95], n_el=2
+        mesh, Beam3rHerm2Line3, material, [0, 0, 0.95], [1, 0, 0.95], n_el=2
     )
     set_2 = create_beam_mesh_line(
-        input_file,
+        mesh,
         Beam3rHerm2Line3,
         material,
         [-0.25, -0.3, 0.85],
@@ -1344,7 +1327,7 @@ def test_meshpy_nurbs_import(
     )
 
     # Add boundary conditions on the beams.
-    input_file.add(
+    mesh.add(
         BoundaryCondition(
             set_1["start"],
             {
@@ -1356,7 +1339,7 @@ def test_meshpy_nurbs_import(
             bc_type=mpy.bc.dirichlet,
         )
     )
-    input_file.add(
+    mesh.add(
         BoundaryCondition(
             set_1["end"],
             {
@@ -1368,7 +1351,7 @@ def test_meshpy_nurbs_import(
             bc_type=mpy.bc.neumann,
         )
     )
-    input_file.add(
+    mesh.add(
         BoundaryCondition(
             set_2["start"],
             {
@@ -1380,7 +1363,7 @@ def test_meshpy_nurbs_import(
             bc_type=mpy.bc.dirichlet,
         )
     )
-    input_file.add(
+    mesh.add(
         BoundaryCondition(
             set_2["end"],
             {
@@ -1404,6 +1387,9 @@ def test_meshpy_nurbs_import(
     nodes = [64]
     add_result_description(input_file, displacements, nodes)
 
+    # Add the mesh to the input file
+    input_file.add(mesh)
+
     # Compare with the reference solution.
     assert_results_equal(get_corresponding_reference_file_path(), input_file)
 
@@ -1421,13 +1407,13 @@ def test_meshpy_stvenantkirchhoff_solid(
 
     material_2 = MaterialStVenantKirchhoff(youngs_modulus=370, nu=0.20, density=5.2e-7)
 
-    # Create input file
-    input_file = InputFile()
-    input_file.add(material_1)
-    input_file.add(material_2)
+    # Create mesh
+    mesh = Mesh()
+    mesh.add(material_1)
+    mesh.add(material_2)
 
     # Compare with the reference file
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 @pytest.mark.parametrize(
@@ -1449,22 +1435,22 @@ def test_meshpy_point_couplings(
 ):
     """Create the input file for the test_point_couplings method."""
 
-    # Create input file.
+    # Create material and mesh
     material = MaterialReissner(radius=0.1, youngs_modulus=1000, interaction_radius=2.0)
-    input_file = InputFile()
+    mesh = Mesh()
 
     # Create a 2x2 grid of beams.
     for i in range(3):
         for j in range(2):
             create_beam_mesh_line(
-                input_file, Beam3rHerm2Line3, material, [j, i, 0.0], [j + 1, i, 0.0]
+                mesh, Beam3rHerm2Line3, material, [j, i, 0.0], [j + 1, i, 0.0]
             )
             create_beam_mesh_line(
-                input_file, Beam3rHerm2Line3, material, [i, j, 0.0], [i, j + 1, 0.0]
+                mesh, Beam3rHerm2Line3, material, [i, j, 0.0], [i, j + 1, 0.0]
             )
 
     # Couple the beams.
-    input_file.couple_nodes(
+    mesh.couple_nodes(
         reuse_matching_nodes=True,
         coupling_type=coupling_type[1],
         coupling_dof_type=coupling_type[2],
@@ -1472,7 +1458,7 @@ def test_meshpy_point_couplings(
 
     assert_results_equal(
         get_corresponding_reference_file_path(additional_identifier=coupling_type[0]),
-        input_file,
+        mesh,
     )
 
 
@@ -1648,13 +1634,14 @@ def test_meshpy_vtk_writer_solid(
             reference_file_base_name="test_create_cubit_input_tube"
         )
     )
+    mesh = input_file.mesh
 
     # Write VTK output.
     ref_file = get_corresponding_reference_file_path(extension="vtu")
     vtk_file = tmp_path / ref_file.name
     if os.path.isfile(vtk_file):  # Todo: Can this check be removed?
         os.remove(vtk_file)
-    input_file.write_vtk(
+    mesh.write_vtk(
         output_name="test_meshpy_vtk_writer", output_directory=tmp_path, binary=False
     )
 
@@ -1676,6 +1663,7 @@ def test_meshpy_vtk_writer_solid_elements(
     input_file.read_yaml(
         get_corresponding_reference_file_path(additional_identifier="import")
     )
+    mesh = input_file.mesh
 
     # Write VTK output.
     ref_file = get_corresponding_reference_file_path(
@@ -1684,7 +1672,7 @@ def test_meshpy_vtk_writer_solid_elements(
     vtk_file = tmp_path / ref_file.name
     if os.path.isfile(vtk_file):  # Todo: Can this check be removed?
         os.remove(vtk_file)
-    input_file.write_vtk(
+    mesh.write_vtk(
         output_name="test_meshpy_vtk_writer_solid_elements",
         output_directory=tmp_path,
         binary=False,
@@ -1817,8 +1805,9 @@ def create_mesh(mesh):
     create_mesh(mesh_ref_2)
     mesh_ref_2.rotate(rotation)
     mesh_ref_2.translate(translate)
-    input_file_ref = InputFile()
-    input_file_ref.add(mesh_ref_1, mesh_ref_2)
+
+    mesh = Mesh()
+    mesh.add(mesh_ref_1, mesh_ref_2)
 
     # Now copy the first mesh and add them together in the input file.
     mesh_copy_1 = Mesh()
@@ -1826,12 +1815,13 @@ def create_mesh(mesh):
     mesh_copy_2 = mesh_copy_1.copy()
     mesh_copy_2.rotate(rotation)
     mesh_copy_2.translate(translate)
-    input_file_copy = InputFile()
-    input_file_copy.add(mesh_copy_1, mesh_copy_2)
+
+    mesh_copy = Mesh()
+    mesh_copy.add(mesh_copy_1, mesh_copy_2)
 
     # Check that the input files are the same.
     # TODO: add reference file check here as well
-    assert_results_equal(input_file_ref, input_file_copy)
+    assert_results_equal(mesh, mesh_copy)
 
 
 def test_meshpy_mesh_add_checks():
@@ -1858,6 +1848,7 @@ def test_meshpy_mesh_add_checks():
     mesh.add(geometry_set)
 
     # Add the objects again and check for errors.
+    # TODO catch and test error messages
     with pytest.raises(ValueError):
         mesh.add(node)
     with pytest.raises(ValueError):
@@ -1877,7 +1868,7 @@ def test_meshpy_check_two_couplings(
     correctly, therefore we check this here."""
 
     # Create mesh object
-    mesh = InputFile()
+    mesh = Mesh()
     mat = MaterialReissner()
     mesh.add(mat)
 
@@ -1907,7 +1898,7 @@ def test_meshpy_check_multiple_node_penalty_coupling(
     """
 
     # Create mesh object
-    mesh = InputFile()
+    mesh = Mesh()
     mat = MaterialReissner()
     mesh.add(mat)
 
@@ -1936,7 +1927,7 @@ def test_meshpy_check_double_elements(
     """Check if there are overlapping elements in a mesh."""
 
     # Create mesh object.
-    mesh = InputFile()
+    mesh = Mesh()
     mat = MaterialReissner()
     mesh.add(mat)
 
@@ -1980,7 +1971,7 @@ def test_meshpy_check_overlapping_coupling_nodes(check):
     """
 
     # Create mesh object.
-    mesh = InputFile()
+    mesh = Mesh()
     mat = MaterialReissner()
     mesh.add(mat)
 
@@ -2031,7 +2022,7 @@ def test_meshpy_userdefined_boundary_condition(
 ):
     """Check if an user defined boundary condition can be added."""
 
-    mesh = InputFile()
+    mesh = Mesh()
 
     mat = MaterialReissner()
     sets = create_beam_mesh_line(mesh, Beam3rHerm2Line3, mat, [0, 0, 0], [1, 2, 3])
@@ -2052,5 +2043,7 @@ def test_meshpy_display_pyvista(get_corresponding_reference_file_path):
     """
 
     mpy.import_mesh_full = True
-    mesh = create_beam_to_solid_conditions_model(get_corresponding_reference_file_path)
-    _ = mesh.display_pyvista(resolution=3)
+    input_file = create_beam_to_solid_conditions_model(
+        get_corresponding_reference_file_path
+    )
+    _ = input_file.mesh.display_pyvista(resolution=3)
diff --git a/tests/test_nurbs.py b/tests/test_nurbs.py
index a560667c..c7f31b35 100644
--- a/tests/test_nurbs.py
+++ b/tests/test_nurbs.py
@@ -23,8 +23,8 @@
 
 import numpy as np
 
+from meshpy.core.mesh import Mesh
 from meshpy.core.rotation import Rotation
-from meshpy.four_c.input_file import InputFile
 from meshpy.four_c.material import MaterialStVenantKirchhoff
 from meshpy.mesh_creation_functions.nurbs_generic import add_geomdl_nurbs_to_mesh
 from meshpy.mesh_creation_functions.nurbs_geometries import (
@@ -48,8 +48,8 @@ def test_nurbs_hollow_cylinder_segment_2d(
         1.74, 2.46, np.pi * 5 / 6, n_ele_u=2, n_ele_v=3
     )
 
-    # Create input file
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material
     mat = MaterialStVenantKirchhoff(youngs_modulus=50, nu=0.19, density=5.3e-7)
@@ -60,16 +60,16 @@ def test_nurbs_hollow_cylinder_segment_2d(
     )
 
     patch_set = add_geomdl_nurbs_to_mesh(
-        input_file,
+        mesh,
         surf_obj,
         material=mat,
         element_description=element_description,
     )
 
-    input_file.add(patch_set)
+    mesh.add(patch_set)
 
     # Compare with the reference file
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_nurbs_flat_plate_2d(
@@ -80,8 +80,8 @@ def test_nurbs_flat_plate_2d(
     # Create the surface of a flat plate
     surf_obj = create_nurbs_flat_plate_2d(0.75, 0.91, n_ele_u=2, n_ele_v=5)
 
-    # Create input file
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material
     mat = MaterialStVenantKirchhoff(youngs_modulus=710, nu=0.19, density=5.3e-7)
@@ -92,16 +92,16 @@ def test_nurbs_flat_plate_2d(
     )
 
     patch_set = add_geomdl_nurbs_to_mesh(
-        input_file,
+        mesh,
         surf_obj,
         material=mat,
         element_description=element_description,
     )
 
-    input_file.add(patch_set)
+    mesh.add(patch_set)
 
     # Compare with the reference file
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_nurbs_brick(assert_results_equal, get_corresponding_reference_file_path):
@@ -110,8 +110,8 @@ def test_nurbs_brick(assert_results_equal, get_corresponding_reference_file_path
     # Create the surface of a flat plate
     vol_obj = create_nurbs_brick(1.5, 3.0, 2.4, n_ele_u=2, n_ele_v=3, n_ele_w=4)
 
-    # Create input file
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material
     mat = MaterialStVenantKirchhoff(youngs_modulus=710, nu=0.19, density=5.3e-7)
@@ -120,16 +120,16 @@ def test_nurbs_brick(assert_results_equal, get_corresponding_reference_file_path
     element_description = "GP 3 3 3"
 
     patch_set = add_geomdl_nurbs_to_mesh(
-        input_file,
+        mesh,
         vol_obj,
         material=mat,
         element_description=element_description,
     )
 
-    input_file.add(patch_set)
+    mesh.add(patch_set)
 
     # Compare with the reference file
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_nurbs_rotation_nurbs_surface(
@@ -142,8 +142,8 @@ def test_nurbs_rotation_nurbs_surface(
         1.74, 2.46, np.pi * 3 / 4, n_ele_u=5, n_ele_v=2
     )
 
-    # Create input file
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Create patch set
     mat = MaterialStVenantKirchhoff(youngs_modulus=650, nu=0.20, density=4.2e-7)
@@ -153,18 +153,18 @@ def test_nurbs_rotation_nurbs_surface(
     )
 
     patch_set = add_geomdl_nurbs_to_mesh(
-        input_file,
+        mesh,
         surf_obj,
         material=mat,
         element_description=element_description,
     )
 
-    input_file.add(patch_set)
+    mesh.add(patch_set)
 
-    input_file.rotate(Rotation([1, 2, 3], np.pi * 7 / 6))
+    mesh.rotate(Rotation([1, 2, 3], np.pi * 7 / 6))
 
     # Compare with the reference file
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_nurbs_translate_nurbs_surface(
@@ -175,8 +175,8 @@ def test_nurbs_translate_nurbs_surface(
     # Create the surface
     surf_obj = create_nurbs_flat_plate_2d(0.87, 1.35, n_ele_u=2, n_ele_v=3)
 
-    # Create input file
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Create patch set
     mat = MaterialStVenantKirchhoff(youngs_modulus=430, nu=0.10, density=4.2e-7)
@@ -186,18 +186,18 @@ def test_nurbs_translate_nurbs_surface(
     )
 
     patch_set = add_geomdl_nurbs_to_mesh(
-        input_file,
+        mesh,
         surf_obj,
         material=mat,
         element_description=element_description,
     )
 
-    input_file.add(patch_set)
+    mesh.add(patch_set)
 
-    input_file.translate([-1.6, -2.3, 3.7])
+    mesh.translate([-1.6, -2.3, 3.7])
 
     # Compare with the reference file
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_nurbs_cylindrical_shell_sector(
@@ -210,24 +210,24 @@ def test_nurbs_cylindrical_shell_sector(
         2.3, np.pi / 3, 1.7, n_ele_u=3, n_ele_v=5
     )
 
-    # Create input file
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Add material
     mat = MaterialStVenantKirchhoff()
 
     # Create patch set
     patch_set = add_geomdl_nurbs_to_mesh(
-        input_file,
+        mesh,
         surf_obj,
         material=mat,
         element_description="dummy",
     )
 
-    input_file.add(patch_set)
+    mesh.add(patch_set)
 
     # Compare with the reference file
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_nurbs_couple_nurbs_meshes(
@@ -235,8 +235,8 @@ def test_nurbs_couple_nurbs_meshes(
 ):
     """Test the coupling of NURBS surface meshes."""
 
-    # Create input file
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Create the first surface object
     surf_obj_1 = create_nurbs_hollow_cylinder_segment_2d(
@@ -251,15 +251,15 @@ def test_nurbs_couple_nurbs_meshes(
     )
 
     patch_set_1 = add_geomdl_nurbs_to_mesh(
-        input_file,
+        mesh,
         surf_obj_1,
         material=mat,
         element_description=element_description,
     )
 
-    input_file.add(patch_set_1)
+    mesh.add(patch_set_1)
 
-    input_file.rotate(Rotation([0, 0, 1], np.pi / 3))
+    mesh.rotate(Rotation([0, 0, 1], np.pi / 3))
 
     # Create the second surface object
     surf_obj_2 = create_nurbs_hollow_cylinder_segment_2d(
@@ -267,18 +267,18 @@ def test_nurbs_couple_nurbs_meshes(
     )
 
     patch_set_2 = add_geomdl_nurbs_to_mesh(
-        input_file,
+        mesh,
         surf_obj_2,
         material=mat,
         element_description=element_description,
     )
 
-    input_file.add(patch_set_2)
+    mesh.add(patch_set_2)
 
-    input_file.couple_nodes(reuse_matching_nodes=True)
+    mesh.couple_nodes(reuse_matching_nodes=True)
 
     # Compare with the reference file
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_nurbs_sphere_surface(
@@ -286,8 +286,8 @@ def test_nurbs_sphere_surface(
 ):
     """Test the creating of the base patch of the surface of a sphere."""
 
-    # Create input file
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Create the base of a sphere
     surf_obj = create_nurbs_sphere_surface(1, n_ele_u=3, n_ele_v=2)
@@ -300,16 +300,16 @@ def test_nurbs_sphere_surface(
     )
 
     patch_set = add_geomdl_nurbs_to_mesh(
-        input_file,
+        mesh,
         surf_obj,
         material=mat,
         element_description=element_description,
     )
 
-    input_file.add(patch_set)
+    mesh.add(patch_set)
 
     # Compare with the reference file
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_nurbs_string_types(
@@ -318,8 +318,8 @@ def test_nurbs_string_types(
     """Test the creating of a NURBS with strings for the element and material
     definition."""
 
-    # Create input file
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Create the base of a sphere
     surf_obj = create_nurbs_flat_plate_2d(1, 3, n_ele_u=3, n_ele_v=2)
@@ -328,17 +328,17 @@ def test_nurbs_string_types(
     mat = MaterialStVenantKirchhoff()
 
     patch_set = add_geomdl_nurbs_to_mesh(
-        input_file,
+        mesh,
         surf_obj,
         material=mat,
         element_string="STRING_TYPE",
         element_description="STRING_DESCRIPTION",
     )
 
-    input_file.add(patch_set)
+    mesh.add(patch_set)
 
     # Compare with the reference file
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_nurbs_hemisphere_surface(
@@ -346,8 +346,8 @@ def test_nurbs_hemisphere_surface(
 ):
     """Test the creation of the surface of a hemisphere."""
 
-    # Create input file
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Create the base of a sphere
     surfs = create_nurbs_hemisphere_surface(2.5, n_ele_uv=2)
@@ -362,16 +362,16 @@ def test_nurbs_hemisphere_surface(
     # Add the patch sets of every surface section of the hemisphere to the input file
     for surf in surfs:
         patch_set = add_geomdl_nurbs_to_mesh(
-            input_file,
+            mesh,
             surf,
             material=mat,
             element_description=element_description,
         )
 
-        input_file.add(patch_set)
+        mesh.add(patch_set)
 
     # Compare with the reference file
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
 
 
 def test_nurbs_torus_surface(
@@ -379,8 +379,8 @@ def test_nurbs_torus_surface(
 ):
     """Test the creation of a torus."""
 
-    # Create input file
-    input_file = InputFile()
+    # Create mesh
+    mesh = Mesh()
 
     # Create the surface of a torus
     surfs = create_nurbs_torus_surface(1, 0.5, n_ele_u=2, n_ele_v=3)
@@ -393,13 +393,13 @@ def test_nurbs_torus_surface(
     # Add the patch sets of every surface section of the torus to the input file
     for surf in surfs:
         patch_set = add_geomdl_nurbs_to_mesh(
-            input_file,
+            mesh,
             surf,
             material=mat,
             element_description=dummy_string,
         )
 
-        input_file.add(patch_set)
+        mesh.add(patch_set)
 
     # Compare with the reference file
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
diff --git a/tests/test_performance.py b/tests/test_performance.py
index c8163760..aebf2c8c 100644
--- a/tests/test_performance.py
+++ b/tests/test_performance.py
@@ -120,7 +120,7 @@ def create_large_beam_mesh(n_x, n_y, n_z, n_el):
     """Create a beam grid on the domain (1 x 1 x 1) with (nx * ny * nz) "grid
     cells"."""
 
-    mesh = InputFile()
+    mesh = Mesh()
     material = MaterialReissner(radius=0.25 / np.max([n_x, n_y, n_z]))
 
     for i_x in range(n_x + 1):


Reviews:
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-11T15:27:34Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-11T15:29:37Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-11T15:32:20Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR removes the inheritance of InputFile from the Mesh class and updates the tests and internal APIs accordingly. Key changes include replacing direct InputFile instantiations with Mesh() in tests, updating calls to add objects (e.g. functions, boundary conditions) via the mesh attribute, and modifying internal methods in InputFile to delegate mesh operations to a composed Mesh object.

### Reviewed Changes

Copilot reviewed 10 out of 12 changed files in this pull request and generated 1 comment.

<details>
<summary>Show a summary per file</summary>

| File                                | Description                                                               |
| ----------------------------------- | ------------------------------------------------------------------------- |
| tests/test_performance.py           | Replaced "InputFile()" with "Mesh()" for creating performance test meshes |
| tests/test_nurbs.py                 | Changed InputFile usage to Mesh for NURBS tests and updated add() calls    |
| tests/test_meshpy.py                | Systematic migration from InputFile to Mesh across multiple mesh tests     |
| tests/test_mesh_creation_functions.py | Updated mesh creation functions to use Mesh instantiation consistently  |
| tests/test_four_c_simulation.py     | Modified simulation tests to use Mesh and add it to the InputFile           |
| tests/test_four_c.py                | Adjusted tests for 4C simulation by delegating mesh operations to Mesh      |
| src/meshpy/four_c/input_file.py     | Updated InputFile to use composition (mesh attribute) instead of inheritance |
| tests/conftest.py                   | Extended type hints in comparison functions for Mesh objects              |
</details>


<details>
<summary>Files not reviewed (2)</summary>

* **tests/reference-files/test_meshpy_comments_in_solid.4C.yaml**: Language not supported
* **tests/reference-files/test_meshpy_comments_in_solid_initial.4C.yaml**: Language not supported
</details>

<details>
<summary>Comments suppressed due to low confidence (2)</summary>

**tests/test_performance.py:123**
* Verify that replacing 'InputFile()' with 'Mesh()' here aligns with downstream test expectations, and update any related documentation to clarify the new test setup.
```
mesh = Mesh()
```
**tests/test_meshpy.py:179**
* Ensure that the migration from 'InputFile()' to 'Mesh()' is applied consistently, and that all function calls and node references correctly use the new 'mesh' attribute.
```
mesh = Mesh()
```
</details>


state: COMMENTED
author_association: NONE
submitted_at: 2025-04-30T16:44:46Z
user: login: isteinbrecher
body: Thanks a lot for the effort @davidrudlstorfer and the clean implementation (also adapting the comments in the testing code!). I only have some minor comments.

I am also not sure about the best way to do big changes like this, but for now I appreciate this way, as it makes the review a lot easier. Also, I think only a small portion of tests will have to be changed again later (basically the ones that import an existing input file) as most of the changed test are already aligning with the envisioned future API.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T06:06:35Z
user: login: isteinbrecher
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T06:10:14Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T08:00:15Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T08:00:39Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T08:02:14Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T08:03:18Z
user: login: isteinbrecher
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T11:59:16Z
user: login: isteinbrecher
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T11:59:49Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T13:52:26Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T13:54:45Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T13:55:30Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T13:58:58Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:07:10Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:07:39Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:08:07Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:10:27Z
user: login: isteinbrecher
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:20:41Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:22:29Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:28:41Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:30:43Z
user: login: isteinbrecher
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:31:55Z
user: login: isteinbrecher
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:34:32Z
user: login: isteinbrecher
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:36:32Z
user: login: isteinbrecher
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:37:50Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:38:25Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-05T14:41:27Z
user: login: isteinbrecher
body: Thanks a lot for the effort @davidrudlstorfer , I only have one small question, but that does not hinder this PR.


> The main problem with many tests is that we import some existing files with a "full import" or not. Then we can no longer simply compare the mesh or the input file because in the end we have to check both. This should be refactored once the mesh moves out of the input file.

I agree. Maybe we can do the following: The function that reads in existing meshes (I more and more think it makes sense that this is a function and not a class method of `InputFile`) should always return two objects:
```python
input_file, mesh = read_existing_yaml_or_whatever_suitable_name(full_import=True/False)
```
If `full_import` is `True`, then the `mesh` will be filled with the relevant data, if `full_import` is `False`, then the mesh will be empty. Then we could have the following logic in testing:
```python
@pytest.mark.parametrize("full_import", [True, False])
def test_space_time_straight(full_import, assert_results_requal):
    input_file, mesh = read_existing_yaml_or_whatever_suitable_name(full_import= full_import)
    # Actually logic of the test -> here we add to mesh
    input_file.add(mesh)
    assert_results_requal(reference, input_file)
```
This should work right away for both input types.
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-05T15:59:12Z
user: login: davidrudlstorfer
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-06T07:05:11Z

Review comments:
diff_hunk: @@ -112,10 +112,10 @@ def test_meshpy_rotations(assert_results_equal):
     """Check if the Mesh function rotation gives the same results as rotating
     each node it self."""
 
-    mesh_1 = InputFile()
+    mesh_1 = Mesh()
     create_test_mesh(mesh_1)
 
-    mesh_2 = InputFile()
+    mesh_2 = Mesh()
     create_test_mesh(mesh_2)
path: tests/test_meshpy.py
user: login: davidrudlstorfer
body: @isteinbrecher one general question I wanted to discuss before proceeding to adjust the tests

In my opinion, in the future MeshPy should work without fourcipp, i.e., a 4C input file. For that it would be ideal if we could run tests on Meshes.

Currently we are not able to compare meshes within the testing framework. Do you share this opinion that we should test it without any overhead?

Maybe you have additional ideas, etc.
created_at: 2025-04-11T15:27:33Z
author_association: COLLABORATOR
diff_hunk: @@ -112,10 +112,10 @@ def test_meshpy_rotations(assert_results_equal):
     """Check if the Mesh function rotation gives the same results as rotating
     each node it self."""
 
-    mesh_1 = InputFile()
+    mesh_1 = Mesh()
     create_test_mesh(mesh_1)
 
-    mesh_2 = InputFile()
+    mesh_2 = Mesh()
     create_test_mesh(mesh_2)
path: tests/test_meshpy.py
user: login: davidrudlstorfer
body: If we share this opinion I would add an option that we can compare individual Meshes in our testing suite.

This somehow contradicts our discussion we had yesterday - we said we mainly wanted to focus on tests with reference files.

But in my opinion it's not that nice that we will always depend on fourcipp.

Maybe create a really generic input file which just dumps the raw data to yaml/json files and compare these?
created_at: 2025-04-11T15:29:37Z
author_association: COLLABORATOR
diff_hunk: @@ -102,7 +102,7 @@ def _get_yaml_geometry_sets(
     return geometry_sets_in_this_section
 
 
-class InputFile(_Mesh):
+class InputFile:
path: src/meshpy/four_c/input_file.py
user: login: davidrudlstorfer
body: And as a final note: I tested the overall working with my minimal example from #318 and everything worked out of the box as we discussed it.
created_at: 2025-04-11T15:32:19Z
author_association: COLLABORATOR
diff_hunk: @@ -206,7 +206,7 @@ def __init__(self, *, yaml_file: _Optional[_Path] = None, cubit=None):
                 into this input file.
         """
 
-        super().__init__()
+        self.mesh = _Mesh()
path: src/meshpy/four_c/input_file.py
user: login: Copilot
body: Update the class documentation to reflect that 'InputFile' now uses composition via its 'mesh' attribute instead of inheriting from '_Mesh', to improve clarity for future maintainers.
created_at: 2025-04-30T16:44:46Z
author_association: NONE
diff_hunk: @@ -205,7 +206,7 @@ def test_cosserat_curve_mesh_warp(
             Rotation([0, 0, 1], -0.5 * np.pi) * Rotation([0, 1, 0], -0.5 * np.pi)
         ),
     )
-
+    print("###", type(mesh), mesh)
path: tests/test_cosserat_curve.py
user: login: isteinbrecher
body: Should this be removed?
created_at: 2025-04-30T19:19:33Z
author_association: COLLABORATOR
diff_hunk: @@ -629,72 +639,69 @@ def test_four_c_beam_to_solid(
 
     # Load a solid
     mpy.import_mesh_full = True
-    input_file = InputFile(
+    mesh = InputFile(
         yaml_file=get_corresponding_reference_file_path(
             reference_file_base_name="test_create_cubit_input_block"
         )
-    )
+    ).mesh
 
     # The yaml file already contains the beam-to-solid boundary conditions
     # for the solid. We don't need them in this test case, as we want to
     # create them again. Thus, we have to delete them here.
-    input_file.boundary_conditions[
+    mesh.boundary_conditions[
         (mpy.bc.beam_to_solid_volume_meshtying, mpy.geo.volume)
     ].clear()
-    input_file.boundary_conditions[
+    mesh.boundary_conditions[
         (mpy.bc.beam_to_solid_surface_meshtying, mpy.geo.surface)
     ].clear()
 
     # Get the geometry set objects representing the geometry from the cubit
     # file.
-    surface_set = input_file.geometry_sets[mpy.geo.surface][0]
-    volume_set = input_file.geometry_sets[mpy.geo.volume][0]
+    surface_set = mesh.geometry_sets[mpy.geo.surface][0]
+    volume_set = mesh.geometry_sets[mpy.geo.volume][0]
 
     # Add the beam
     material = MaterialReissner()
     beam_set_1 = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, material, [0, 0, 0], [0, 0, 1], n_el=1
+        mesh, Beam3rHerm2Line3, material, [0, 0, 0], [0, 0, 1], n_el=1
     )
     beam_set_2 = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, material, [0, 1, 0], [0, 1, 1], n_el=2
+        mesh, Beam3rHerm2Line3, material, [0, 1, 0], [0, 1, 1], n_el=2
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         volume_set,
         beam_set_1["line"],
         mpy.bc.beam_to_solid_volume_meshtying,
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         volume_set,
         beam_set_2["line"],
         mpy.bc.beam_to_solid_volume_meshtying,
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         surface_set,
         beam_set_2["line"],
         mpy.bc.beam_to_solid_surface_meshtying,
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         surface_set,
         beam_set_1["line"],
         mpy.bc.beam_to_solid_surface_meshtying,
     )
 
-    # Check results
-    print(get_corresponding_reference_file_path())
path: tests/test_four_c.py
user: login: isteinbrecher
body: We’re these printouts already here?
created_at: 2025-04-30T19:23:51Z
author_association: COLLABORATOR
diff_hunk: @@ -725,6 +732,13 @@ def test_four_c_import_non_consecutive_geometry_sets(
 ):
     """Test that we can import non-consecutively numbered geometry sets."""
 
+    # TODO rework this test once the input file is refactored, i.e.
path: tests/test_four_c.py
user: login: isteinbrecher
body: I am not sure I fully understand this comment - we already add new geometry sets in this test.
created_at: 2025-04-30T19:25:54Z
author_association: COLLABORATOR
diff_hunk: @@ -354,9 +361,8 @@ def test_four_c_simulation_beam_and_solid_tube(
     nodes = [32, 69]
     add_result_description(input_file, displacements, nodes)
 
-    # Call get_unique_geometry_sets to check that this does not affect the
path: tests/test_four_c_simulation.py
user: login: isteinbrecher
body: Why is this removed?
created_at: 2025-04-30T19:27:26Z
author_association: COLLABORATOR
diff_hunk: @@ -183,10 +183,10 @@ def add_point_neuman_condition_to_input_file(
 
     # Add the function to the input file, if they are not previously added.
     for function in function_array:
-        input_file.add(function)
+        input_file.mesh.add(function)
path: src/meshpy/four_c/dbc_monitor.py
user: login: isteinbrecher
body: I think that we want to pass a `Mesh` to this function and not an `InputFile`. @knarfnitram what do you think?
created_at: 2025-05-05T05:13:47Z
author_association: COLLABORATOR
diff_hunk: @@ -565,7 +571,7 @@ def test_four_c_simulation_rotated_beam_axis(
         and couples them with a coupling.
     """
 
-    # Create input file.
+    # Create input file
     input_file = InputFile()
path: tests/test_four_c_simulation.py
user: login: isteinbrecher
body: In test cases like this you could already introduce a separate mesh and input file - then this test won't have to be changed again once we remove the mesh as a class variable of `InputFile`. (You are already doing this in some other tests)
created_at: 2025-05-05T05:21:23Z
author_association: COLLABORATOR
diff_hunk: @@ -593,10 +586,7 @@ def test_mesh_creation_functions_node_continuation(
         mesh, Beam3rHerm2Line3, mat, beam_set["end"], 2.3, n_el=3
     )
 
-    # Check the geometry
-    input_file = InputFile()
-    input_file.add(mesh)
-    assert_results_equal(get_corresponding_reference_file_path(), input_file)
+    assert_results_equal(get_corresponding_reference_file_path(), mesh)
path: tests/test_mesh_creation_functions.py
user: login: isteinbrecher
body: I like how this simplifies!
created_at: 2025-05-05T05:25:13Z
author_association: COLLABORATOR
path: tests/test_mesh_creation_functions.py
user: login: isteinbrecher
body: This file still has a `from meshpy.four_c.input_file import InputFile` - can we get rid of that? From a conceptional point of view we don't need this import here - if some tests really require it, we should move them to the `four_c` tests.
created_at: 2025-05-05T05:28:48Z
author_association: COLLABORATOR
diff_hunk: @@ -120,7 +120,7 @@ def create_large_beam_mesh(n_x, n_y, n_z, n_el):
     """Create a beam grid on the domain (1 x 1 x 1) with (nx * ny * nz) "grid
     cells"."""
 
-    mesh = InputFile()
+    mesh = Mesh()
path: tests/test_performance.py
user: login: isteinbrecher
body: I think this will lead to a failure of the performance tests - but I would still leave this here as we should adapt the part that calls this function once CubitPy is running again.

General thought to myself. Maybe we should create an additional performance test that does not require cubit and can be run on the self hosted runners.
created_at: 2025-05-05T05:39:29Z
author_association: COLLABORATOR
path: tests/test_meshpy.py
user: login: isteinbrecher
body: Same comment as for the previous file, we should evaluate the tests that really require `InputFile` in this file - but we don't have to do this in this PR.
created_at: 2025-05-05T05:42:07Z
author_association: COLLABORATOR
diff_hunk: @@ -285,7 +254,15 @@ def base_test_mesh_translations(*, import_full=False, radius=None, reflect=True)
         mpy.import_mesh_full = import_full
 
         # Create the mesh.
-        mesh = InputFile(
+
+        # TODO rework this test once the input file is refactored, i.e.
+        # input_file, mesh = InputFile(yaml_file=...)
+        # ... add different geometry sets to the mesh
+        # input_file.add(mesh)
+        # this way it is tested exhaustively rather than accessing the mesh
+        # in the input file during the beam mesh creation
+
+        input_file = InputFile(
             yaml_file=get_corresponding_reference_file_path(
                 reference_file_base_name="4C_input_solid_cuboid"
             )
path: tests/test_meshpy.py
user: login: isteinbrecher
body: ```suggestion
        input_file = InputFile(
            yaml_file=get_corresponding_reference_file_path(
                reference_file_base_name="4C_input_solid_cuboid"
            )
        mesh = input_file.mesh
```
Then you only have to change this initialisation and not all the references to `mesh` in upcoming PRs.
created_at: 2025-05-05T05:48:13Z
author_association: COLLABORATOR
diff_hunk: @@ -285,7 +254,15 @@ def base_test_mesh_translations(*, import_full=False, radius=None, reflect=True)
         mpy.import_mesh_full = import_full
 
         # Create the mesh.
-        mesh = InputFile(
+
+        # TODO rework this test once the input file is refactored, i.e.
path: tests/test_meshpy.py
user: login: isteinbrecher
body: Also here, I don't fully understand this comment.
created_at: 2025-05-05T05:48:54Z
author_association: COLLABORATOR
diff_hunk: @@ -1038,13 +1018,10 @@ def test_meshpy_replace_nodes_geometry_set(
     mat = MaterialReissner(radius=0.1, youngs_modulus=1)
     rot = Rotation([1, 2, 43], 213123)
 
-    def create_mesh():
-        """Create two empty meshes."""
-        return InputFile(), InputFile()
-
path: tests/test_meshpy.py
user: login: isteinbrecher
body: I have no idea why I came up with this solution in the first place 😅
created_at: 2025-05-05T05:51:33Z
author_association: COLLABORATOR
diff_hunk: @@ -1194,6 +1173,13 @@ def create_mesh():
 def create_beam_to_solid_conditions_model(get_corresponding_reference_file_path):
     """Create the input file for the beam-to-solid input conditions tests."""
 
+    # TODO rework this test once the input file is refactored, i.e.
path: tests/test_meshpy.py
user: login: isteinbrecher
body: Same here.
created_at: 2025-05-05T05:52:25Z
author_association: COLLABORATOR
diff_hunk: @@ -1817,21 +1809,25 @@ def create_mesh(mesh):
     create_mesh(mesh_ref_2)
     mesh_ref_2.rotate(rotation)
     mesh_ref_2.translate(translate)
-    input_file_ref = InputFile()
-    input_file_ref.add(mesh_ref_1, mesh_ref_2)
+
+    mesh = Mesh()
+    mesh.add(mesh_ref_1)
+    mesh.add(mesh_ref_2)
path: tests/test_meshpy.py
user: login: isteinbrecher
body: Is there a specific reason why you use `add` twice and not once?
created_at: 2025-05-05T05:54:06Z
author_association: COLLABORATOR
diff_hunk: @@ -1858,6 +1854,7 @@ def test_meshpy_mesh_add_checks():
     mesh.add(geometry_set)
 
     # Add the objects again and check for errors.
+    # TODO catch and test errors
path: tests/test_meshpy.py
user: login: isteinbrecher
body: Isn't this exactly what is done here? Or are you referring to actually checking the error message?
created_at: 2025-05-05T05:55:08Z
author_association: COLLABORATOR
diff_hunk: @@ -1194,6 +1173,13 @@ def create_mesh():
 def create_beam_to_solid_conditions_model(get_corresponding_reference_file_path):
path: tests/test_meshpy.py
user: login: isteinbrecher
body: I think this function should also take the `import_full` argument and should return an `InputFile` and a `Mesh` (if `input_full==False` the mesh will be empty). Can you please add a comment here, so we do this once we witch the input file input functionality?
created_at: 2025-05-05T06:02:09Z
author_association: COLLABORATOR
diff_hunk: @@ -565,7 +571,7 @@ def test_four_c_simulation_rotated_beam_axis(
         and couples them with a coupling.
     """
 
-    # Create input file.
+    # Create input file
     input_file = InputFile()
path: tests/test_four_c_simulation.py
user: login: isteinbrecher
body: Edit: I think this is the only test where this comment applies.
created_at: 2025-05-05T06:10:14Z
author_association: COLLABORATOR
diff_hunk: @@ -205,7 +206,7 @@ def test_cosserat_curve_mesh_warp(
             Rotation([0, 0, 1], -0.5 * np.pi) * Rotation([0, 1, 0], -0.5 * np.pi)
         ),
     )
-
+    print("###", type(mesh), mesh)
path: tests/test_cosserat_curve.py
user: login: davidrudlstorfer
body: Thanks!
created_at: 2025-05-05T08:00:14Z
author_association: COLLABORATOR
diff_hunk: @@ -629,72 +639,69 @@ def test_four_c_beam_to_solid(
 
     # Load a solid
     mpy.import_mesh_full = True
-    input_file = InputFile(
+    mesh = InputFile(
         yaml_file=get_corresponding_reference_file_path(
             reference_file_base_name="test_create_cubit_input_block"
         )
-    )
+    ).mesh
 
     # The yaml file already contains the beam-to-solid boundary conditions
     # for the solid. We don't need them in this test case, as we want to
     # create them again. Thus, we have to delete them here.
-    input_file.boundary_conditions[
+    mesh.boundary_conditions[
         (mpy.bc.beam_to_solid_volume_meshtying, mpy.geo.volume)
     ].clear()
-    input_file.boundary_conditions[
+    mesh.boundary_conditions[
         (mpy.bc.beam_to_solid_surface_meshtying, mpy.geo.surface)
     ].clear()
 
     # Get the geometry set objects representing the geometry from the cubit
     # file.
-    surface_set = input_file.geometry_sets[mpy.geo.surface][0]
-    volume_set = input_file.geometry_sets[mpy.geo.volume][0]
+    surface_set = mesh.geometry_sets[mpy.geo.surface][0]
+    volume_set = mesh.geometry_sets[mpy.geo.volume][0]
 
     # Add the beam
     material = MaterialReissner()
     beam_set_1 = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, material, [0, 0, 0], [0, 0, 1], n_el=1
+        mesh, Beam3rHerm2Line3, material, [0, 0, 0], [0, 0, 1], n_el=1
     )
     beam_set_2 = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, material, [0, 1, 0], [0, 1, 1], n_el=2
+        mesh, Beam3rHerm2Line3, material, [0, 1, 0], [0, 1, 1], n_el=2
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         volume_set,
         beam_set_1["line"],
         mpy.bc.beam_to_solid_volume_meshtying,
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         volume_set,
         beam_set_2["line"],
         mpy.bc.beam_to_solid_volume_meshtying,
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         surface_set,
         beam_set_2["line"],
         mpy.bc.beam_to_solid_surface_meshtying,
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         surface_set,
         beam_set_1["line"],
         mpy.bc.beam_to_solid_surface_meshtying,
     )
 
-    # Check results
-    print(get_corresponding_reference_file_path())
path: tests/test_four_c.py
user: login: davidrudlstorfer
body: Yes - I think we can remove them
created_at: 2025-05-05T08:00:39Z
author_association: COLLABORATOR
diff_hunk: @@ -183,10 +183,10 @@ def add_point_neuman_condition_to_input_file(
 
     # Add the function to the input file, if they are not previously added.
     for function in function_array:
-        input_file.add(function)
+        input_file.mesh.add(function)
path: src/meshpy/four_c/dbc_monitor.py
user: login: davidrudlstorfer
body: I actually changed the entire dbc_monitor file to use a mesh instead of an input file. The changes got pretty big - therefore, I reverted them back. This should be done in another PR 
created_at: 2025-05-05T08:02:14Z
author_association: COLLABORATOR
path: tests/test_mesh_creation_functions.py
user: login: davidrudlstorfer
body: This was just a leftover. Good catch - I've removed it
created_at: 2025-05-05T08:03:18Z
author_association: COLLABORATOR
diff_hunk: @@ -183,10 +183,10 @@ def add_point_neuman_condition_to_input_file(
 
     # Add the function to the input file, if they are not previously added.
     for function in function_array:
-        input_file.add(function)
+        input_file.mesh.add(function)
path: src/meshpy/four_c/dbc_monitor.py
user: login: isteinbrecher
body: Sounds good to me. For now the test are passing and the changes are not too big, so lets merge this and then do it in a separate PR before we remove the `.mesh` class variable.
created_at: 2025-05-05T11:59:15Z
author_association: COLLABORATOR
diff_hunk: @@ -629,72 +639,69 @@ def test_four_c_beam_to_solid(
 
     # Load a solid
     mpy.import_mesh_full = True
-    input_file = InputFile(
+    mesh = InputFile(
         yaml_file=get_corresponding_reference_file_path(
             reference_file_base_name="test_create_cubit_input_block"
         )
-    )
+    ).mesh
 
     # The yaml file already contains the beam-to-solid boundary conditions
     # for the solid. We don't need them in this test case, as we want to
     # create them again. Thus, we have to delete them here.
-    input_file.boundary_conditions[
+    mesh.boundary_conditions[
         (mpy.bc.beam_to_solid_volume_meshtying, mpy.geo.volume)
     ].clear()
-    input_file.boundary_conditions[
+    mesh.boundary_conditions[
         (mpy.bc.beam_to_solid_surface_meshtying, mpy.geo.surface)
     ].clear()
 
     # Get the geometry set objects representing the geometry from the cubit
     # file.
-    surface_set = input_file.geometry_sets[mpy.geo.surface][0]
-    volume_set = input_file.geometry_sets[mpy.geo.volume][0]
+    surface_set = mesh.geometry_sets[mpy.geo.surface][0]
+    volume_set = mesh.geometry_sets[mpy.geo.volume][0]
 
     # Add the beam
     material = MaterialReissner()
     beam_set_1 = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, material, [0, 0, 0], [0, 0, 1], n_el=1
+        mesh, Beam3rHerm2Line3, material, [0, 0, 0], [0, 0, 1], n_el=1
     )
     beam_set_2 = create_beam_mesh_line(
-        input_file, Beam3rHerm2Line3, material, [0, 1, 0], [0, 1, 1], n_el=2
+        mesh, Beam3rHerm2Line3, material, [0, 1, 0], [0, 1, 1], n_el=2
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         volume_set,
         beam_set_1["line"],
         mpy.bc.beam_to_solid_volume_meshtying,
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         volume_set,
         beam_set_2["line"],
         mpy.bc.beam_to_solid_volume_meshtying,
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         surface_set,
         beam_set_2["line"],
         mpy.bc.beam_to_solid_surface_meshtying,
     )
     add_beam_interaction_condition(
-        input_file,
+        mesh,
         surface_set,
         beam_set_1["line"],
         mpy.bc.beam_to_solid_surface_meshtying,
     )
 
-    # Check results
-    print(get_corresponding_reference_file_path())
path: tests/test_four_c.py
user: login: isteinbrecher
body: Definitely, no idea how they ended up here 😂
created_at: 2025-05-05T11:59:49Z
author_association: COLLABORATOR
diff_hunk: @@ -725,6 +732,13 @@ def test_four_c_import_non_consecutive_geometry_sets(
 ):
     """Test that we can import non-consecutively numbered geometry sets."""
 
+    # TODO rework this test once the input file is refactored, i.e.
path: tests/test_four_c.py
user: login: davidrudlstorfer
body: What I wanted to say with this comment is that in the future there is no more mesh in the Input File and we need to adjust the function to load a mesh from an existing input file (so it is returned)

I already have a local branch which is based on this branch which removes the mesh completely from the input file. Then this should be tackled.
created_at: 2025-05-05T13:52:26Z
author_association: COLLABORATOR
diff_hunk: @@ -565,7 +571,7 @@ def test_four_c_simulation_rotated_beam_axis(
         and couples them with a coupling.
     """
 
-    # Create input file.
+    # Create input file
     input_file = InputFile()
path: tests/test_four_c_simulation.py
user: login: davidrudlstorfer
body: What's your vision with this test?

Down below a separate mesh is created for each iteration of the loop - in the end its added to the input file.

A separate mesh just for the function and then add the mesh to the input file? If that's what you meant I can add this
created_at: 2025-05-05T13:54:45Z
author_association: COLLABORATOR
diff_hunk: @@ -120,7 +120,7 @@ def create_large_beam_mesh(n_x, n_y, n_z, n_el):
     """Create a beam grid on the domain (1 x 1 x 1) with (nx * ny * nz) "grid
     cells"."""
 
-    mesh = InputFile()
+    mesh = Mesh()
path: tests/test_performance.py
user: login: davidrudlstorfer
body: Why do you think that this would fail?
created_at: 2025-05-05T13:55:30Z
author_association: COLLABORATOR
path: tests/test_meshpy.py
user: login: davidrudlstorfer
body: I've now switched one more test in this file to be based on a pure mesh. The remaining tests in this file all import a mesh via an input file. This should be tackled in the upcoming PR where I remove the mesh from the input file
created_at: 2025-05-05T13:58:58Z
author_association: COLLABORATOR
diff_hunk: @@ -285,7 +254,15 @@ def base_test_mesh_translations(*, import_full=False, radius=None, reflect=True)
         mpy.import_mesh_full = import_full
 
         # Create the mesh.
-        mesh = InputFile(
+
+        # TODO rework this test once the input file is refactored, i.e.
+        # input_file, mesh = InputFile(yaml_file=...)
+        # ... add different geometry sets to the mesh
+        # input_file.add(mesh)
+        # this way it is tested exhaustively rather than accessing the mesh
+        # in the input file during the beam mesh creation
+
+        input_file = InputFile(
             yaml_file=get_corresponding_reference_file_path(
                 reference_file_base_name="4C_input_solid_cuboid"
             )
path: tests/test_meshpy.py
user: login: davidrudlstorfer
body: I've changed this in all places where it is possible
created_at: 2025-05-05T14:07:10Z
author_association: COLLABORATOR
diff_hunk: @@ -285,7 +254,15 @@ def base_test_mesh_translations(*, import_full=False, radius=None, reflect=True)
         mpy.import_mesh_full = import_full
 
         # Create the mesh.
-        mesh = InputFile(
+
+        # TODO rework this test once the input file is refactored, i.e.
path: tests/test_meshpy.py
user: login: davidrudlstorfer
body: Remove it and extracted the mesh from the input file
created_at: 2025-05-05T14:07:39Z
author_association: COLLABORATOR
diff_hunk: @@ -1194,6 +1173,13 @@ def create_mesh():
 def create_beam_to_solid_conditions_model(get_corresponding_reference_file_path):
     """Create the input file for the beam-to-solid input conditions tests."""
 
+    # TODO rework this test once the input file is refactored, i.e.
path: tests/test_meshpy.py
user: login: davidrudlstorfer
body: Same here, extracted the mesh
created_at: 2025-05-05T14:08:07Z
author_association: COLLABORATOR
diff_hunk: @@ -1817,21 +1809,25 @@ def create_mesh(mesh):
     create_mesh(mesh_ref_2)
     mesh_ref_2.rotate(rotation)
     mesh_ref_2.translate(translate)
-    input_file_ref = InputFile()
-    input_file_ref.add(mesh_ref_1, mesh_ref_2)
+
+    mesh = Mesh()
+    mesh.add(mesh_ref_1)
+    mesh.add(mesh_ref_2)
path: tests/test_meshpy.py
user: login: davidrudlstorfer
body: Did not know that this was possible - changed both occurrences to a single `add`
created_at: 2025-05-05T14:10:27Z
author_association: COLLABORATOR
diff_hunk: @@ -725,6 +732,13 @@ def test_four_c_import_non_consecutive_geometry_sets(
 ):
     """Test that we can import non-consecutively numbered geometry sets."""
 
+    # TODO rework this test once the input file is refactored, i.e.
path: tests/test_four_c.py
user: login: isteinbrecher
body: Makes complete sene, I think I got caught up by the "add geometry sets" part. I am fine with leaving this in a few places of the code and tackling it in a follow up PR.
created_at: 2025-05-05T14:20:41Z
author_association: COLLABORATOR
diff_hunk: @@ -1858,6 +1854,7 @@ def test_meshpy_mesh_add_checks():
     mesh.add(geometry_set)
 
     # Add the objects again and check for errors.
+    # TODO catch and test errors
path: tests/test_meshpy.py
user: login: davidrudlstorfer
body: I meant that we should also check the raised error messages - I've added this to the comment
created_at: 2025-05-05T14:22:29Z
author_association: COLLABORATOR
diff_hunk: @@ -1194,6 +1173,13 @@ def create_mesh():
 def create_beam_to_solid_conditions_model(get_corresponding_reference_file_path):
path: tests/test_meshpy.py
user: login: davidrudlstorfer
body: Added a comment
created_at: 2025-05-05T14:28:41Z
author_association: COLLABORATOR
diff_hunk: @@ -285,7 +254,15 @@ def base_test_mesh_translations(*, import_full=False, radius=None, reflect=True)
         mpy.import_mesh_full = import_full
 
         # Create the mesh.
-        mesh = InputFile(
+
+        # TODO rework this test once the input file is refactored, i.e.
+        # input_file, mesh = InputFile(yaml_file=...)
+        # ... add different geometry sets to the mesh
+        # input_file.add(mesh)
+        # this way it is tested exhaustively rather than accessing the mesh
+        # in the input file during the beam mesh creation
+
+        input_file = InputFile(
             yaml_file=get_corresponding_reference_file_path(
                 reference_file_base_name="4C_input_solid_cuboid"
             )
path: tests/test_meshpy.py
user: login: davidrudlstorfer
body: In this case it is not possible due to the testing setup. This should also be refactored once we switch the inputfile/mesh logic of an import.
created_at: 2025-05-05T14:30:43Z
author_association: COLLABORATOR
diff_hunk: @@ -565,7 +571,7 @@ def test_four_c_simulation_rotated_beam_axis(
         and couples them with a coupling.
     """
 
-    # Create input file.
+    # Create input file
     input_file = InputFile()
path: tests/test_four_c_simulation.py
user: login: isteinbrecher
body: My intention was, to have
```python
input_file = InputFile()
mesh = Mesh()
```
at the beginning, then you can can simply add the meshes created in the loop
```python
for i in range(3):
    mesh_line = Mesh() # this is currently named mesh, but mesh_line makes more sense
    ...
    mesh.add(mesh_line)
```
If we do it like this, then I think we don't have to touch this test again if we remove the `.mesh` class variable from `InputFile`. But since we will have to touch one way or another, I will leave it up to you if you want to do this here or in the one of next PRs.

> What's your vision with this test?

Very good question, it is actually really interesting having a closer look at all the old tests again. Some of the are quite strange... My goal going further is to have more actual unit tests than pure framework tests - I guess there you can see where I initially "learned" how to do software testing.
created_at: 2025-05-05T14:31:55Z
author_association: COLLABORATOR
diff_hunk: @@ -120,7 +120,7 @@ def create_large_beam_mesh(n_x, n_y, n_z, n_el):
     """Create a beam grid on the domain (1 x 1 x 1) with (nx * ny * nz) "grid
     cells"."""
 
-    mesh = InputFile()
+    mesh = Mesh()
path: tests/test_performance.py
user: login: isteinbrecher
body: Because at some point we actually write the input file to disc during testing - but I think this is already broken since the YAML change (we did not yet reactivate the performance tests in this file). But I might be wrong and the tests pass. Anyhow, we will have to have a look at these tests once CubitPy is useable with MeshPy again, to get them up and running again, and maybe adapt the test times. So please choose the option that is easiest for you with this test.
created_at: 2025-05-05T14:34:32Z
author_association: COLLABORATOR
diff_hunk: @@ -1817,21 +1809,25 @@ def create_mesh(mesh):
     create_mesh(mesh_ref_2)
     mesh_ref_2.rotate(rotation)
     mesh_ref_2.translate(translate)
-    input_file_ref = InputFile()
-    input_file_ref.add(mesh_ref_1, mesh_ref_2)
+
+    mesh = Mesh()
+    mesh.add(mesh_ref_1)
+    mesh.add(mesh_ref_2)
path: tests/test_meshpy.py
user: login: isteinbrecher
body: Yes this is possible, also to add nested lists like: `mesh.add([mesh_1, [mesh_2, mesh_3]], mesh_4, mesh_5)` - but I have no idea if that is actually a good or bad interface, so if this leads to issues at some point we can always change that.

created_at: 2025-05-05T14:36:32Z
author_association: COLLABORATOR
path: tests/test_meshpy.py
user: login: isteinbrecher
body: Great, thanks for looking into this.
created_at: 2025-05-05T14:37:50Z
author_association: COLLABORATOR
diff_hunk: @@ -565,7 +571,7 @@ def test_four_c_simulation_rotated_beam_axis(
         and couples them with a coupling.
     """
 
-    # Create input file.
+    # Create input file
     input_file = InputFile()
path: tests/test_four_c_simulation.py
user: login: davidrudlstorfer
body: Ah that's what you've meant. I've added it accordingly.
created_at: 2025-05-05T14:38:25Z
author_association: COLLABORATOR
diff_hunk: @@ -354,9 +361,8 @@ def test_four_c_simulation_beam_and_solid_tube(
     nodes = [32, 69]
     add_result_description(input_file, displacements, nodes)
 
-    # Call get_unique_geometry_sets to check that this does not affect the
path: tests/test_four_c_simulation.py
user: login: davidrudlstorfer
body: whoopsie - added it back
created_at: 2025-05-05T14:41:27Z
author_association: COLLABORATOR
diff_hunk: @@ -1858,6 +1848,7 @@ def test_meshpy_mesh_add_checks():
     mesh.add(geometry_set)
 
     # Add the objects again and check for errors.
+    # TODO catch and test error messages
path: tests/test_meshpy.py
user: login: isteinbrecher
body: Do you mean that we should also check for the error message here?
created_at: 2025-05-05T15:52:32Z
author_association: COLLABORATOR
diff_hunk: @@ -1858,6 +1848,7 @@ def test_meshpy_mesh_add_checks():
     mesh.add(geometry_set)
 
     # Add the objects again and check for errors.
+    # TODO catch and test error messages
path: tests/test_meshpy.py
user: login: davidrudlstorfer
body: Yes
created_at: 2025-05-06T07:05:10Z
author_association: COLLABORATOR

PR comments:
user: login: davidrudlstorfer
created_at: 2025-04-30T16:43:57Z
body: @isteinbrecher as discussed today - this would be ready for review 😅

The changes to the testing framework were a lot of manual labor - which will be touched again with the follow up PR's. Therefore, I am still torn if it is the better approach to split everything up 🫣

This should at least be easier to review and I am curious about your opinion:)
author_association: COLLABORATOR
user: login: davidrudlstorfer
created_at: 2025-04-30T16:47:28Z
body: And I almost forgot these points:

- This is a breaking change ⚠️ now input file does not inherit the mesh anymore
- And I've also deleted one test because this should be useless with yaml?
author_association: COLLABORATOR
user: login: davidrudlstorfer
created_at: 2025-04-30T16:49:51Z
body: And my final comment 😅

In the future we could provide a pytest fixture to create a mesh, this way we could remove all of the

```python
# Create mesh
mesh = Mesh()
```
and just use 

```python
test_something_in_meshpy(mesh):
```
author_association: COLLABORATOR
user: login: davidrudlstorfer
created_at: 2025-05-05T14:43:42Z
body: @isteinbrecher this would be ready for review again - I hope I've incorporated all of you suggestions accordingly. Thanks for the review.

The main problem with many tests is that we import some existing files with a "full import" or not. Then we can no longer simply compare the mesh or the input file because in the end we have to check both. This should be refactored once the mesh moves out of the input file.
author_association: COLLABORATOR
user: login: davidrudlstorfer
created_at: 2025-05-06T07:07:16Z
body: > Thanks a lot for the effort @davidrudlstorfer , I only have one small question, but that does not hinder this PR.
> 
> > The main problem with many tests is that we import some existing files with a "full import" or not. Then we can no longer simply compare the mesh or the input file because in the end we have to check both. This should be refactored once the mesh moves out of the input file.
> 
> 
> I agree. Maybe we can do the following: The function that reads in existing meshes (I more and more think it makes sense that this is a function and not a class method of `InputFile`) should always return two objects:
> 
> `input_file, mesh = read_existing_yaml_or_whatever_suitable_name(full_import=True/False)`
> 
> If `full_import` is `True`, then the `mesh` will be filled with the relevant data, if `full_import` is `False`, then the mesh will be empty. Then we could have the following logic in testing:
> ```
> @pytest.mark.parametrize("full_import", [True, False])
> def test_space_time_straight(full_import, assert_results_requal):
>     input_file, mesh = read_existing_yaml_or_whatever_suitable_name(full_import= full_import)
>     # Actually logic of the test -> here we add to mesh
>     input_file.add(mesh)
>     assert_results_requal(reference, input_file)
> ```
> 
> This should work right away for both input types.

Thanks for the nice summary - that's exactly what I have imagined for the testing and also the function to read an existing input file.

The following PR will now remove the mesh completely from the input file. Therefore, I am merging this
author_association: COLLABORATOR