diff --git a/apps/tutorials/codegen/HeatEquationKernelWithMaterial.py b/apps/tutorials/codegen/HeatEquationKernelWithMaterial.py
index 6348e6356c6452ee94067047be9eea2f8298721a..ea80613df51bf5d4ea953162b37f1711c12b35c8 100644
--- a/apps/tutorials/codegen/HeatEquationKernelWithMaterial.py
+++ b/apps/tutorials/codegen/HeatEquationKernelWithMaterial.py
@@ -1,12 +1,12 @@
 import os
 import sys
-script_dir = os.path.dirname(__file__)
-walberla_dir = os.path.join(script_dir, '..', '..', '..', '..', 'walberla')
-sys.path.append(walberla_dir)
 import sympy as sp
 import pystencils as ps
 from pystencils_walberla import CodeGeneration, generate_sweep
-from src.materials.alloys import Ti6Al4V
+script_dir = os.path.dirname(__file__)
+walberla_dir = os.path.join(script_dir, '..', '..', '..', '..', 'walberla')
+sys.path.append(walberla_dir)
+# from src.materials.alloys import Ti6Al4V
 from src.materials.alloys.SS316L import SS316L
 from src.materials.assignment_converter import assignment_converter
 
@@ -27,7 +27,13 @@ with CodeGeneration() as ctx:
     Ti6Al4V = SS316L.create_SS316L(u.center())
 
     # Convert assignments to pystencils format
+    print("Print statements")
+    print(Ti6Al4V.thermal_diffusivity)
+    print(Ti6Al4V.thermal_diffusivity.expr)
+    print(Ti6Al4V.thermal_diffusivity.assignments)
     subexp, subs = assignment_converter(Ti6Al4V.thermal_diffusivity.assignments)
+    print(subexp)
+    print(subs)
 
     subexp.append(ps.Assignment(thermal_diffusivity, Ti6Al4V.thermal_diffusivity.expr))
 
diff --git a/src/materials/alloys/Alloy.py b/src/materials/alloys/Alloy.py
index 8b638106d9ec9b090857d2b6a0513d250ad89cc1..43f5fe9edf2e77b4fd3adf8da9c08e02939eb088 100644
--- a/src/materials/alloys/Alloy.py
+++ b/src/materials/alloys/Alloy.py
@@ -22,15 +22,15 @@ class Alloy:
 
         Optional properties:
             density (PropertyTypes): Density of the alloy.
+            dynamic_viscosity (PropertyTypes): Dynamic viscosity of the alloy.
             heat_capacity (PropertyTypes): Heat capacity of the alloy.
             heat_conductivity (PropertyTypes): Heat conductivity of the alloy.
+            kinematic_viscosity (PropertyTypes): Kinematic viscosity of the alloy.
             latent_heat_of_fusion (PropertyTypes): Latent heat of fusion of the alloy.
             latent_heat_of_vaporization (PropertyTypes): Latent heat of vaporization of the alloy.
+            surface_tension (PropertyTypes): Surface tension of the alloy.
             thermal_diffusivity (PropertyTypes): Thermal diffusivity of the alloy.
             thermal_expansion_coefficient (PropertyTypes): Thermal expansion coefficient of the alloy.
-            dynamic_viscosity (PropertyTypes): Dynamic viscosity of the alloy.
-            kinematic_viscosity (PropertyTypes): Kinematic viscosity of the alloy.
-            surface_tension (PropertyTypes): Surface tension of the alloy.
     """
     elements: List[Element]
     composition: ArrayTypes  # List of fractions summing to 1.0
@@ -44,15 +44,16 @@ class Alloy:
 
     # Optional properties with default values
     density: PropertyTypes = None
+    dynamic_viscosity: PropertyTypes = None
     heat_capacity: PropertyTypes = None
     heat_conductivity: PropertyTypes = None
+    kinematic_viscosity: PropertyTypes = None
     latent_heat_of_fusion: PropertyTypes = None
     latent_heat_of_vaporization: PropertyTypes = None
+    surface_tension: PropertyTypes = None
     thermal_diffusivity: PropertyTypes = None
     thermal_expansion_coefficient: PropertyTypes = None
-    dynamic_viscosity: PropertyTypes = None
-    kinematic_viscosity: PropertyTypes = None
-    surface_tension: PropertyTypes = None
+
 
     def solidification_interval(self) -> Tuple[float, float]:
         """
@@ -65,14 +66,17 @@ class Alloy:
 
     def __post_init__(self) -> None:
         """
-        Initializes properties based on elemental composition and validates the composition.
+        Initializes properties based on elemental composition and validates the composition and phase transition temperatures.
 
         Raises:
-            ValueError: If the sum of the composition array does not equal 1.
+            ValueError: If the sum of the composition array does not equal 1 or if the solidus temperature is greater than the liquidus temperature.
         """
         if not np.isclose(sum(self.composition), 1.0, atol=1e-12):
             raise ValueError(f"The sum of the composition array must be 1.0, got {sum(self.composition)}")
 
+        if self.temperature_solidus > self.temperature_liquidus:
+            raise ValueError("The solidus temperature must be less than or equal to the liquidus temperature.")
+
         self.atomic_number = interpolate_atomic_number(self.elements, self.composition)
         self.atomic_mass = interpolate_atomic_mass(self.elements, self.composition)
         self.temperature_boil = interpolate_temperature_boil(self.elements, self.composition)
@@ -109,7 +113,7 @@ if __name__ == '__main__':
 
         # Invalid Property Assignment
         try:
-            Ti64.heat_conductivity = "invalid_value"
+            Ti64.heat_conductivity = "invalid_value"  # type: ignore
         except TypeError as e:
             print(f"Invalid Property Assignment Test Passed: {e}")
 
diff --git a/src/materials/alloys/SS316L/SS316L.py b/src/materials/alloys/SS316L/SS316L.py
index c79a425595e1781b202dff9599d4c0b6afbb4e6a..2ca4198d617609156af31a97669753b2dbb2335a 100644
--- a/src/materials/alloys/SS316L/SS316L.py
+++ b/src/materials/alloys/SS316L/SS316L.py
@@ -1,11 +1,13 @@
+import numpy as np
 import sympy as sp
 from pathlib import Path
 from typing import Union
 from src.materials.alloys.Alloy import Alloy
 from src.materials.elements import Fe, Cr, Mn, Ni
-from src.materials.models import thermal_diffusivity_by_heat_conductivity
+from src.materials.models import thermal_diffusivity_by_heat_conductivity, density_by_thermal_expansion, MaterialPropertyWrapper
 from src.materials.data_handler.data_handler import read_data, celsius_to_kelvin, thousand_times
 from src.materials.interpolators import interpolate_property
+from src.materials.constants import Constants
 
 
 def create_SS316L(T: Union[float, sp.Symbol]) -> Alloy:
@@ -16,7 +18,7 @@ def create_SS316L(T: Union[float, sp.Symbol]) -> Alloy:
         T (Union[float, sp.Symbol]): Temperature as a symbolic variable or numeric value.
 
     Returns:
-        Tuple[Alloy, Tuple, Tuple, Tuple, Tuple]: Initialized SS316L alloy with physical properties and temperature arrays.
+        Alloy: Initialized SS316L alloy with physical properties.
 
     Notes:
         - **Data Units**: All input data should be in SI units. If data is not in SI units, it must be converted. For example:
@@ -32,6 +34,8 @@ def create_SS316L(T: Union[float, sp.Symbol]) -> Alloy:
     Example:
         If you have temperature data in Celsius and property data in non-SI units, convert the temperature to Kelvin and property values to SI units before using them.
     """
+    if isinstance(T, float) and (T < 0 or T > 3000):  # Example valid range: 0 to 3000 K
+        raise ValueError("Invalid temperature. Temperature must be within the valid range (0 to 3000 K).")
     # Define the alloy with specific elemental composition and phase transition temperatures
     SS316L = Alloy(
         elements=[Fe, Cr, Mn, Ni],
@@ -47,7 +51,7 @@ def create_SS316L(T: Union[float, sp.Symbol]) -> Alloy:
     # Paths to data files using relative paths
     density_data_file_path = str(base_dir / 'density_temperature.txt')
     heat_capacity_data_file_path = str(base_dir / 'heat_capacity_temperature.txt')
-    heat_conductivity_data_file_path = str(base_dir / 'heat_conductivity_temperature.txt')
+    heat_conductivity_data_file_path = str(base_dir / '..' / 'SS316L' / 'heat_conductivity_temperature.txt')
 
     # Read temperature and material property data from the files
     density_temp_array, density_array = read_data(density_data_file_path)
@@ -69,24 +73,78 @@ def create_SS316L(T: Union[float, sp.Symbol]) -> Alloy:
     heat_conductivity_temp_array = celsius_to_kelvin(heat_conductivity_temp_array)
 
     # Perform interpolation for each property
-    SS316L.density = interpolate_property(T, density_temp_array, density_array)
+    print('density')
+    # SS316L.density = interpolate_property(T, density_temp_array, density_array)
+    SS316L.density = MaterialPropertyWrapper(density_by_thermal_expansion(
+        T, Constants.temperature_room, 7940.7476, SS316L.thermal_expansion_coefficient))
+    print('SS316L.density: ', SS316L.density)
+    print('heat_capacity')
     SS316L.heat_capacity = interpolate_property(T, heat_capacity_temp_array, heat_capacity_array)
+    print('heat_conductivity')
     SS316L.heat_conductivity = interpolate_property(T, heat_conductivity_temp_array, heat_conductivity_array)
 
     # Calculate thermal diffusivity from thermal conductivity, density, and heat capacity
-    SS316L.thermal_diffusivity = interpolate_property(T, density_temp_array,
+    print('thermal_diffusivity')
+
+    if isinstance(T, sp.Symbol):
+        thermal_diffusivity_array = thermal_diffusivity_by_heat_conductivity(
+            SS316L.heat_conductivity.evalf(T, density_temp_array),
+            SS316L.density.evalf(T, heat_conductivity_temp_array),
+            # SS316L.density.expr,
+            SS316L.heat_capacity.evalf(T, heat_conductivity_temp_array)
+        )
+        print('SS316L.heat_conductivity.evalf(T, density_temp_array): ', (SS316L.heat_conductivity.evalf(T, density_temp_array)))  # <class 'numpy.ndarray'>
+        print('SS316L.density.expr: ', type(SS316L.density.expr))  # <class 'sympy.core.mul.Mul'>
+        print('SS316L.heat_capacity.evalf(T, heat_conductivity_temp_array): ', (SS316L.heat_capacity.evalf(T, heat_conductivity_temp_array)))  # <class 'numpy.ndarray'>
+        print('thermal_diffusivity_array: ', type(thermal_diffusivity_array))  # <class 'sympy.tensor.array.dense_ndim_array.ImmutableDenseNDimArray'>
+
+        # Calculate thermal diffusivity values for each temperature in the array
+        thermal_diffusivity_array1 = np.array([
+            (thermal_diffusivity_by_heat_conductivity(
+                (SS316L.heat_conductivity.evalf(T, temp)),
+                # SS316L.density.evalf(T, temp),
+                SS316L.density.expr,
+                (SS316L.heat_capacity.evalf(T, temp))
+            ))
+            for temp in density_temp_array
+        ])
+
+        print('thermal_diffusivity_array1 type:', type(thermal_diffusivity_array1))
+        print('thermal_diffusivity_array1 dtype:', thermal_diffusivity_array1.dtype)
+        print('thermal_diffusivity_array1 shape:', thermal_diffusivity_array1.shape)
+        print('First few values of thermal_diffusivity_array1:', thermal_diffusivity_array1[:5])
+
+        SS316L.thermal_diffusivity = interpolate_property(T, density_temp_array, thermal_diffusivity_array)
+
+    elif isinstance(T, float):
+    # elif isinstance(T, sp.Symbol):
+        # Calculate thermal diffusivity from thermal conductivity, density, and heat capacity
+        thermal_diffusivity_array2 = []
+        for temp in heat_conductivity_temp_array:
+            k = SS316L.heat_conductivity.evalf(T, temp)
+            rho = SS316L.density.evalf(T, temp)
+            cp = SS316L.heat_capacity.evalf(T, temp)
+            thermal_diffusivity2 = thermal_diffusivity_by_heat_conductivity(k, rho, cp)
+            thermal_diffusivity_array2.append(thermal_diffusivity2)
+
+        # Interpolate thermal diffusivity using the temperature array and calculated values
+        SS316L.thermal_diffusivity = interpolate_property(T, density_temp_array, thermal_diffusivity_array2)
+
+
+    '''SS316L.thermal_diffusivity = interpolate_property(T, density_temp_array,
                                                       thermal_diffusivity_by_heat_conductivity(
                                                           SS316L.heat_conductivity.evalf(T, density_temp_array),
-                                                          SS316L.density.evalf(T, density_temp_array),
-                                                          SS316L.heat_capacity.evalf(T, density_temp_array)
-                                                      ))
+                                                          # SS316L.density.evalf(T, heat_conductivity_temp_array),
+                                                          SS316L.density.expr,
+                                                          SS316L.heat_capacity.evalf(T, heat_conductivity_temp_array)
+                                                      ))'''
 
     return SS316L
 
 
 if __name__ == '__main__':
     Temp = sp.Symbol('Temp')
-    alloy = create_SS316L(Temp)
+    alloy = create_SS316L(300.15)
 
     # Print the composition of each element in the alloy
     for i in range(len(alloy.composition)):
diff --git a/src/materials/alloys/Ti6Al4V.py b/src/materials/alloys/Ti6Al4V.py
index 6ba95809333b53cd0da0f361f9de0438678f8218..49b6d8ec09b9fbd1fd649b7fdaf056c0ee9e3e22 100644
--- a/src/materials/alloys/Ti6Al4V.py
+++ b/src/materials/alloys/Ti6Al4V.py
@@ -4,7 +4,7 @@ from src.materials.alloys.Alloy import Alloy
 from src.materials.constants import Constants
 from src.materials.elements import Ti, Al, V
 from src.materials.interpolators import interpolate_equidistant, interpolate_lookup, interpolate_property
-from src.materials.models import density_by_thermal_expansion, thermal_diffusivity_by_heat_conductivity
+from src.materials.models import density_by_thermal_expansion, thermal_diffusivity_by_heat_conductivity, MaterialPropertyWrapper
 from src.materials.typedefs import MaterialProperty
 
 
@@ -54,7 +54,7 @@ def create_Ti6Al4V(T: Union[float, sp.Symbol]) -> Alloy:
     )
 
     # Compute density based on thermal expansion and other constants
-    Ti6Al4V.density = MaterialProperty(density_by_thermal_expansion(
+    Ti6Al4V.density = MaterialPropertyWrapper(density_by_thermal_expansion(
         T, Constants.temperature_room, 4430, Ti6Al4V.thermal_expansion_coefficient))
 
     # Interpolate heat conductivity based on temperature
@@ -66,9 +66,16 @@ def create_Ti6Al4V(T: Union[float, sp.Symbol]) -> Alloy:
                                                        thermal_diffusivity_by_heat_conductivity(
                                                            Ti6Al4V.heat_conductivity.evalf(T, Ti6Al4V.solidification_interval()),
                                                            Ti6Al4V.density.expr,
+                                                           # Ti6Al4V.density.evalf(T, Ti6Al4V.solidification_interval()),
                                                            Ti6Al4V.heat_capacity
                                                        ))
 
+    print('thermal_diffusivity_by_heat_conductivity: ', thermal_diffusivity_by_heat_conductivity(
+        Ti6Al4V.heat_conductivity.evalf(T, Ti6Al4V.solidification_interval()),
+        Ti6Al4V.density.expr,
+        # Ti6Al4V.density.evalf(T, Ti6Al4V.solidification_interval()),
+        Ti6Al4V.heat_capacity
+    ))
     return Ti6Al4V
 
 
diff --git a/src/materials/assignment_converter.py b/src/materials/assignment_converter.py
index 043634c55363e556ef89c06feb88073a97b52bd1..e397304cad5ad714bbc7799fbae475013e7223fb 100644
--- a/src/materials/assignment_converter.py
+++ b/src/materials/assignment_converter.py
@@ -4,28 +4,43 @@ import pystencils as ps
 from typing import List, Dict, Tuple, Union
 from pystencils.types.quick import Arr, Fp
 from pystencils.sympyextensions.typed_sympy import CastFunc
-from src.materials.typedefs import Assignment
+from src.materials.typedefs import Assignment, ArrayTypes
 
 
-def type_mapping(type_str: str) -> Union[np.dtype, Arr]:
+def type_mapping(type_str: str, length: int) -> Union[np.dtype, Arr]:
     """
     Maps a string representation of a type to a corresponding numpy or pystencils data type.
 
     Parameters:
         type_str (str): The string representation of the type (e.g., "double[]", "float[]").
+        length (int): The length of the array for array types.
 
     Returns:
         Union[np.dtype, Arr]: The corresponding numpy or pystencils data type.
-        - "double[]" maps to Arr(Fp(64)) which represents a 64-bit floating point array.
-        - "float[]" maps to Arr(Fp(32)) which represents a 32-bit floating point array.
-        - Other type strings are mapped to corresponding numpy data types.
+
+    Examples:
+        >>> type_mapping("double[]", 5)
+        PsArrayType(element_type=PsIeeeFloatType( width=64, const=True ), size=5, const=False)
+        >>> type_mapping("float[]", 3)
+        PsArrayType(element_type=PsIeeeFloatType( width=32, const=True ), size=3, const=False)
+        >>> type_mapping("double", 1)
+        dtype('float64')
     """
     if type_str == "double[]":
-        return Arr(Fp(64))  # 64-bit floating point array
+        return Arr(Fp(64, const=True), length=length)  # 64-bit floating point array
     elif type_str == "float[]":
-        return Arr(Fp(32))  # 32-bit floating point array
+        return Arr(Fp(32, const=True), length=length)  # 32-bit floating point array
+    elif type_str == "double":
+        return np.dtype('float64')
+    elif type_str == "float":
+        return np.dtype('float32')
+    elif type_str == "int":
+        return np.dtype('int32')
+    elif type_str == "bool":
+        return np.dtype('bool')
     else:
-        return np.dtype(type_str)  # Default to numpy data type
+        # return np.dtype(type_str)  # Default to numpy data type
+        raise ValueError(f"Unsupported type string: {type_str}")
 
 
 def assignment_converter(assignment_list: List[Assignment]) \
@@ -50,7 +65,15 @@ def assignment_converter(assignment_list: List[Assignment]) \
     subs = {}
 
     for assignment in assignment_list:
-        ps_type = type_mapping(assignment.lhs_type)
+        if assignment.lhs is None or assignment.rhs is None or assignment.lhs_type is None:
+            raise ValueError("Invalid assignment: lhs, rhs, and lhs_type must not be None")
+
+    for assignment in assignment_list:
+        if isinstance(assignment.rhs, ArrayTypes):
+            length = len(assignment.rhs)
+        else:
+            length = 1
+        ps_type = type_mapping(assignment.lhs_type, length)
         data_symbol = ps.TypedSymbol(assignment.lhs.name, ps_type)
 
         if isinstance(ps_type, Arr):
@@ -78,9 +101,9 @@ if __name__ == '__main__':
 
     # Test type_mapping function
     print("Testing type_mapping function:")
-    print(type_mapping("double[]"))  # Expected: Arr(Fp(64))
-    print(type_mapping("float[]"))   # Expected: Arr(Fp(32))
-    print(type_mapping("int"))       # Expected: np.int32 or equivalent numpy type
+    print(type_mapping("double[]", 5))  # Expected: Arr(Fp(64), length=5)
+    print(type_mapping("float[]", 3))   # Expected: Arr(Fp(32), length=3)
+    print(type_mapping("int", 1))       # Expected: np.int32 or equivalent numpy type
 
     # Test assignment_converter function
     print("\nTesting assignment_converter function:")
diff --git a/src/materials/interpolators.py b/src/materials/interpolators.py
index 21b97788136949eccdd92a6b6b66440ed84d8072..9cb496430790a7582c34b62d05077504da682cbd 100644
--- a/src/materials/interpolators.py
+++ b/src/materials/interpolators.py
@@ -1,6 +1,7 @@
 import numpy as np
 import sympy as sp
-from typing import Union
+from typing import Union, List, Tuple
+from src.materials.models import Wrapper, MaterialPropertyWrapper
 from src.materials.typedefs import Assignment, ArrayTypes, MaterialProperty
 
 COUNT = 0
@@ -10,7 +11,8 @@ def interpolate_equidistant(
         T: Union[float, sp.Symbol],
         T_base: float,
         T_incr: float,
-        v_array: ArrayTypes) -> Union[float, MaterialProperty]:
+        # v_array: ArrayTypes) -> Union[float, MaterialProperty]:  # PropertyTypes
+        v_array: ArrayTypes) -> MaterialProperty:
     """
     Perform equidistant interpolation for symbolic or numeric temperature values.
 
@@ -35,20 +37,20 @@ def interpolate_equidistant(
         sym_idx = sp.symbols(label + '_idx', cls=sp.Idx)
         sym_dec = sp.Symbol(label + "_dec")
 
-        T_base = sp.Float(T_base)
-        T_incr = sp.Float(T_incr)
+        T_base = Wrapper(T_base)  # T_base = sp.Float(T_base)
+        T_incr = Wrapper(T_incr)  # T_incr = sp.Float(T_incr)
 
         pos = (T - T_base) / T_incr
         pos_dec = pos - sp.floor(pos)
 
         result = sp.Piecewise(
-            (sp.Float(float(v_array[0])), T < T_base),
-            (sp.Float(float(v_array[-1])), T >= T_base + (len(v_array) - 1) * T_incr),
+            (Wrapper(v_array[0]), T < T_base),
+            (Wrapper(v_array[-1]), T >= T_base + (len(v_array) - 1) * T_incr),
             ((1 - sym_dec) * sym_data[sym_idx] + sym_dec * sym_data[sym_idx + 1], True)
         )
-
+        print('sym_data, type(sym_data): ', sym_data, type(sym_data))
         sub_expressions = [
-            Assignment(sym_data, tuple(sp.Float(float(v)) for v in v_array), "double[]"),
+            Assignment(sym_data, tuple(Wrapper(v) for v in v_array), "double[]"),
             Assignment(sym_idx, pos, "int"),
             Assignment(sym_dec, pos_dec, "double")
         ]
@@ -61,16 +63,22 @@ def interpolate_equidistant(
         min_temp = T_base
         max_temp = T_base + (n - 1) * T_incr
         if T < min_temp:
-            return float(v_array[0])
+            # return float(v_array[0])
+            # return MaterialProperty(sp.Float(float(v_array[0])), [])
+            return MaterialPropertyWrapper(v_array[0])
         elif T > max_temp:
-            return float(v_array[-1])
+            # return float(v_array[-1])
+            # return MaterialProperty(sp.Float(float(v_array[-1])), [])
+            return MaterialPropertyWrapper(v_array[-1])
 
         pos = (T - T_base) / T_incr
         pos_int = int(pos)
         pos_dec = pos - pos_int
 
         value = (1 - pos_dec) * v_array[pos_int] + pos_dec * v_array[pos_int + 1]
-        return float(value)
+        # return float(value)
+        # return MaterialProperty(sp.Float(float(value)), [])
+        return MaterialPropertyWrapper(value)
 
     else:
         raise ValueError(f"Unsupported type for T: {type(T)}")
@@ -79,7 +87,8 @@ def interpolate_equidistant(
 def interpolate_lookup(
         T: Union[float, sp.Symbol],
         T_array: ArrayTypes,
-        v_array: ArrayTypes) -> Union[float, MaterialProperty]:
+        # v_array: ArrayTypes) -> Union[float, MaterialProperty]:  # PropertyTypes
+        v_array: ArrayTypes) -> MaterialProperty:
     """
     Perform lookup-based interpolation for symbolic or numeric temperature values.
 
@@ -97,8 +106,8 @@ def interpolate_lookup(
         v_array = np.flip(v_array)
 
     if isinstance(T, sp.Symbol):
-        T_array = [sp.Float(float(t)) for t in T_array]
-        v_array = [v if isinstance(v, sp.Expr) else sp.Float(float(v)) for v in v_array]
+        T_array = [Wrapper(t) for t in T_array]
+        v_array = [Wrapper(v) for v in v_array]
 
         conditions = [
             (v_array[0], T < T_array[0]),
@@ -114,12 +123,18 @@ def interpolate_lookup(
         T_array = np.asarray(T_array)
         v_array = np.asarray(v_array)
         if T < T_array[0]:
-            return float(v_array[0])
+            # return float(v_array[0])
+            #nreturn MaterialProperty(sp.Float(float(v_array[0])), [])
+            return MaterialPropertyWrapper(v_array[0])
         elif T > T_array[-1]:
-            return float(v_array[-1])
+            # return float(v_array[-1])
+            # return MaterialProperty(sp.Float(float(v_array[-1])), [])
+            return MaterialPropertyWrapper(v_array[-1])
         else:
-            return float(np.interp(T, T_array, v_array))
-
+            # return float(np.interp(T, T_array, v_array))
+            # return MaterialProperty(sp.Float(float(np.interp(T, T_array, v_array))), [])
+            v = np.interp(T, T_array, v_array)
+            return MaterialPropertyWrapper(v)
     else:
         raise ValueError(f"Invalid input for T: {type(T)}")
 
@@ -141,8 +156,9 @@ def interpolate_property(
         T: Union[float, sp.Symbol],
         temp_array: ArrayTypes,
         prop_array: ArrayTypes,
-        temp_array_limit=6,
-        force_lookup=False) -> Union[float, MaterialProperty]:
+        temp_array_limit: int = 6,
+        # force_lookup=False) -> Union[float, MaterialProperty]:  # PropertyTypes
+        force_lookup: bool = False) -> MaterialProperty:
     """
     Perform interpolation based on the type of temperature array (equidistant or not).
 
@@ -154,6 +170,13 @@ def interpolate_property(
     :return: Interpolated value.
     :raises ValueError: If T_array and v_array lengths do not match.
     """
+    # Ensure that temp_array and prop_array are arrays
+    if not isinstance(temp_array, (Tuple, List, np.ndarray)):
+        raise TypeError(f"Expected temp_array to be a list or array, got {type(temp_array)}")
+
+    if not isinstance(prop_array, (Tuple, List, np.ndarray)):
+        raise TypeError(f"Expected prop_array to be a list or array, got {type(prop_array)}")
+
     if len(temp_array) != len(prop_array):
         raise ValueError("T_array and v_array must have the same length")
 
@@ -164,10 +187,12 @@ def interpolate_property(
     incr = test_equidistant(np.asarray(temp_array))
 
     if force_lookup or incr == 0 or len(temp_array) < temp_array_limit:
+        print('interpolate_lookup')
         return interpolate_lookup(T, temp_array, prop_array)
     else:
+        print('interpolate_equidistant')
         return interpolate_equidistant(
-            T, float(temp_array[0]), float(incr), prop_array)
+            T, float(temp_array[0]), incr, prop_array)
 
 
 if __name__ == '__main__':
@@ -183,59 +208,36 @@ if __name__ == '__main__':
     # Interpolation Lookup Tests
     print("Interpolate Lookup Tests:")
     symbolic_result = interpolate_lookup(Temp, density_temp_array1, density_v_array1)
-    print("Symbolic interpolation result with interpolate_lookup (arrays):",
-          symbolic_result.expr if isinstance(symbolic_result, MaterialProperty) else symbolic_result)
-
+    print("Symbolic interpolation result with interpolate_lookup (arrays):", symbolic_result.expr)
     symbolic_result_list = interpolate_lookup(Temp, density_temp_array2, density_v_array2)
-    print("Symbolic interpolation result with interpolate_lookup (lists):",
-          symbolic_result_list.expr if isinstance(symbolic_result_list, MaterialProperty) else symbolic_result_list)
-
+    print("Symbolic interpolation result with interpolate_lookup (lists):", symbolic_result_list.expr)
     numeric_result = interpolate_lookup(1894.7, density_temp_array2, density_v_array2)
-    print("Numeric interpolation result with interpolate_lookup (arrays):", numeric_result)
-
-    numeric_result_list = interpolate_lookup(1900.2, [200.0, 300.0, 400.0, 500.0, 600.0],
-                                             [50.0, 40.0, 30.0, 20.0, 10.0])
-    print("Numeric interpolation result with interpolate_lookup (lists):", numeric_result_list)
-
-    try:
-        error_result = interpolate_lookup("what's up", density_temp_array2, density_v_array2)
-    except ValueError as e:
-        print("Error:", e)
+    print("Numeric interpolation result with interpolate_lookup (arrays):", numeric_result.expr)
+    numeric_result_list = interpolate_lookup(1900.2, [200.0, 300.0, 400.0, 500.0, 600.0], [50.0, 40.0, 30.0, 20.0, 10.0])
+    print("Numeric interpolation result with interpolate_lookup (lists):", numeric_result_list.expr)
 
     # Interpolation Equidistant Tests
     print("\nInterpolate Equidistant Tests:")
     symbolic_result_eq = interpolate_equidistant(Temp, Temp_base, Temp_incr, density_v_array1)
     print("Symbolic interpolation result with interpolate_equidistant (numpy arrays):", symbolic_result_eq.expr)
-    print("Assignments:", symbolic_result_eq.assignments)
-
     symbolic_result_eq_list = interpolate_equidistant(Temp, Temp_base, Temp_incr, density_v_array2)
     print("Symbolic interpolation result with interpolate_equidistant (lists):", symbolic_result_eq_list.expr)
-    print("Assignments:", symbolic_result_eq_list.assignments)
-
     numeric_result_eq = interpolate_equidistant(1900.2, Temp_base, Temp_incr, density_v_array1)
-    print("Numeric interpolation result with interpolate_equidistant (numpy arrays):", numeric_result_eq)
-
+    print("Numeric interpolation result with interpolate_equidistant (numpy arrays):", numeric_result_eq.expr)
     numeric_result_eq_list = interpolate_equidistant(1900.2, Temp_base, Temp_incr, density_v_array2)
-    print("Numeric interpolation result with interpolate_equidistant (lists):", numeric_result_eq_list)
-
-    try:
-        error_result_eq = interpolate_equidistant("what's up", Temp_base, Temp_incr, density_v_array2)
-    except ValueError as e:
-        print("Error:", e)
+    print("Numeric interpolation result with interpolate_equidistant (lists):", numeric_result_eq_list.expr)
 
     # Consistency Test
     print("\nConsistency Test between interpolate_lookup and interpolate_equidistant:")
     test_temps = [150.0, 200.0, 350.0, 450.0, 550.0, 650.0]
     for TT in test_temps:
-        lookup_result = interpolate_lookup(TT, np.flip(np.array([600.0, 500.0, 400.0, 300.0, 200.0])),
-                                           np.array([10.0, 20.0, 30.0, 40.0, 50.0]))
+        lookup_result = interpolate_lookup(TT, np.flip(np.array([600.0, 500.0, 400.0, 300.0, 200.0])), np.array([10.0, 20.0, 30.0, 40.0, 50.0]))
         equidistant_result = interpolate_equidistant(TT, 200.0, 100.0, np.array([10.0, 20.0, 30.0, 40.0, 50.0]))
         print(f"Temperature {TT}:")
-        print(f"interpolate_lookup result: {lookup_result}")
-        print(f"interpolate_equidistant result: {equidistant_result}")
-
-        if isinstance(lookup_result, (int, float)) and isinstance(equidistant_result, (int, float)):
-            assert np.isclose(lookup_result, equidistant_result), f"Inconsistent results for T = {TT}"
+        print(f"interpolate_lookup result: {lookup_result.expr}")
+        print(f"interpolate_equidistant result: {equidistant_result.expr}")
+        if isinstance(lookup_result.expr, (int, float)) and isinstance(equidistant_result.expr, (int, float)):
+            assert np.isclose(lookup_result.expr, equidistant_result.expr), f"Inconsistent results for T = {TT}"
         else:
             print(f"Skipping comparison for symbolic or non-numeric result at T = {TT}")
 
diff --git a/src/materials/models.py b/src/materials/models.py
index cc1f1f088b2e9438f5ce29e39d7c5a9151661393..56014a9752a79bf0c0a7c34d461c75808d2fb88d 100644
--- a/src/materials/models.py
+++ b/src/materials/models.py
@@ -1,6 +1,31 @@
 import numpy as np
 import sympy as sp
-from typing import Union
+from typing import Union, List
+from src.materials.typedefs import MaterialProperty
+
+
+def Wrapper(value: Union[sp.Expr, int, float, np.ndarray]) -> Union[sp.Expr, List[sp.Expr]]:
+    if isinstance(value, sp.Expr):
+        return sp.sympify(value)
+    if isinstance(value, (int, float)):
+        return sp.Float(float(value))
+    if isinstance(value, np.ndarray):
+        return [sp.Float(float(v)) for v in value]
+        # return [Wrapper(v) for v in value]
+    raise ValueError("Unsupported type for value in Wrapper")
+
+
+'''def MaterialPropertyWrapper(value: Union[sp.Expr, int, float, np.ndarray]) -> MaterialProperty:
+    wrapped_value = Wrapper(value)
+    if isinstance(wrapped_value, list):
+        # Instead of returning a list of MaterialProperties, wrap the entire array
+        return MaterialProperty(np.array(wrapped_value))
+    return MaterialProperty(wrapped_value)'''
+
+
+def MaterialPropertyWrapper(value: Union[sp.Expr, int, float, np.ndarray]) -> MaterialProperty:
+    wrapped_value = Wrapper(value)
+    return MaterialProperty(wrapped_value)
 
 
 def density_by_thermal_expansion(
@@ -24,9 +49,17 @@ def density_by_thermal_expansion(
     :param thermal_expansion_coefficient: Thermal expansion coefficient. Can be a numeric value (float), numpy array (np.ndarray), or a symbolic expression (sp.Expr).
     :return: Density at temperature T. Returns a float if all inputs are numeric; otherwise, returns a numpy array or a sympy expression.
     """
+    # Input validation
+    for param, name in zip([temperature, temperature_base, density_base, thermal_expansion_coefficient], ['temperature', 'temperature_base', 'density_base', 'thermal_expansion_coefficient']):
+        if isinstance(param, (float, int)) and param <= 0:
+            raise ValueError(f"{name.capitalize()} must be positive.")
+        if isinstance(param, np.ndarray) and (param <= 0).any():
+            raise ValueError(f"{name.capitalize()} array contains non-positive values.")
+
     if isinstance(temperature, sp.Symbol) and isinstance(thermal_expansion_coefficient, np.ndarray):
         raise ValueError("This combination is not valid. Provide the temperature data which is used for the thermal_expansion_coefficient and create a new interpolation material property.")
-    return density_base * (1 + thermal_expansion_coefficient * (temperature - temperature_base)) ** (-3)
+    result = density_base * (1 + thermal_expansion_coefficient * (temperature - temperature_base)) ** (-3)
+    return result
 
 
 def thermal_diffusivity_by_heat_conductivity(
@@ -49,8 +82,16 @@ def thermal_diffusivity_by_heat_conductivity(
     :param heat_capacity: Specific heat capacity. Can be a numeric value (float), numpy array (np.ndarray), or a symbolic expression (sp.Expr).
     :return: Thermal diffusivity. Returns a float if all inputs are numeric; otherwise, returns a numpy array or a sympy expression.
     """
+    # Input validation
+    for param, name in zip([heat_conductivity, density, heat_capacity], ['heat_conductivity', 'density', 'heat_capacity']):
+        if isinstance(param, (float, int)) and param <= 0:
+            raise ValueError(f"{name.capitalize()} must be positive.")
+        if isinstance(param, np.ndarray) and (param <= 0).any():
+            raise ValueError(f"{name.capitalize()} array contains non-positive values.")
+
     if isinstance(density, sp.Piecewise) or isinstance(heat_conductivity, sp.Piecewise) or isinstance(heat_capacity, sp.Piecewise):
         raise ValueError("Do not insert piecewise functions here. Prepare the data with corresponding temperature intervals.")
+
     result = heat_conductivity / (density * heat_capacity)
     return result
 
@@ -100,4 +141,86 @@ if __name__ == '__main__':
     c_p_symbolic = sp.Symbol('c_p')
     rho_symbolic = sp.Symbol('rho')
     print(f"Test 6 - Symbolic thermal diffusivity computation: {thermal_diffusivity_by_heat_conductivity(k_symbolic, rho_symbolic, c_p_symbolic)}")
-    # Expected output: Sympy expression for thermal diffusivity as a function of k, rho, and c_p
+
+    # New test case for mixed input types
+    print("\nTest 9: Mixed input types")
+
+    # For density_by_thermal_expansion
+    T_mixed = np.linspace(800, 1000, 3)  # numpy array
+    T_base = 293.15  # float
+    rho_base = 8000.0  # float
+    tec_symbolic = sp.Symbol('alpha')  # symbolic
+
+    try:
+        result_density = density_by_thermal_expansion(T_mixed, T_base, rho_base, tec_symbolic)
+        print(f"Density with mixed inputs: {result_density}")
+    except Exception as e:
+        print(f"Error in density calculation with mixed inputs: {str(e)}")
+
+    # For thermal_diffusivity_by_heat_conductivity
+    k_mixed = np.linspace(20, 30, 3)  # numpy array
+    rho_symbolic = sp.Symbol('rho')  # symbolic
+    cp_float = 500.0  # float
+
+    try:
+        result_diffusivity = thermal_diffusivity_by_heat_conductivity(k_mixed, rho_symbolic, cp_float)
+        print(f"Thermal diffusivity with mixed inputs: {result_diffusivity}")
+    except Exception as e:
+        print(f"Error in thermal diffusivity calculation with mixed inputs: {str(e)}")
+
+    # Test case for all symbolic inputs
+    print("\nTest 10: All symbolic inputs")
+    T_sym = sp.Symbol('T')
+    tec_sym = sp.Symbol('alpha')
+    k_sym = sp.Symbol('k')
+    rho_sym = sp.Symbol('rho')
+    cp_sym = sp.Symbol('cp')
+
+    density_sym = density_by_thermal_expansion(T_sym, T_base, rho_base, tec_sym)
+    print(f"Symbolic density: {density_sym}")
+
+    diffusivity_sym = thermal_diffusivity_by_heat_conductivity(k_sym, rho_sym, cp_sym)
+    print(f"Symbolic thermal diffusivity: {diffusivity_sym}")
+
+    # Test 11: Edge cases with zero and negative values
+    print("\nTest 11: Edge cases with zero and negative values")
+    try:
+        print(density_by_thermal_expansion(-273.15, T_0, rho, tec))  # Absolute zero
+    except ValueError as e:
+        print(f"Handled error for negative temperature: {e}")
+
+    try:
+        print(density_by_thermal_expansion(T_1, T_0, rho, -1e-6))  # Negative TEC
+    except ValueError as e:
+        print(f"Handled error for negative TEC: {e}")
+
+    # Test 12: Large arrays
+    print("\nTest 12: Large arrays")
+    large_T = np.linspace(1000, 2000, 10000)
+    large_k = np.linspace(30, 40, 10000)
+    try:
+        large_density = density_by_thermal_expansion(large_T, T_0, rho, tec)
+        print(f"Large density array: {large_density}")
+    except Exception as e:
+        print(f"Error with large density array: {e}")
+
+    try:
+        large_diffusivity = thermal_diffusivity_by_heat_conductivity(large_k, rho, c_p)
+        print(f"Large diffusivity array: {large_diffusivity}")
+    except Exception as e:
+        print(f"Error with large diffusivity array: {e}")
+
+    # Test 13: Complex numbers (if applicable)
+    print("\nTest 13: Complex numbers")
+    complex_T = sp.Symbol('T') + sp.I
+    try:
+        complex_density = density_by_thermal_expansion(complex_T, T_0, rho, tec)
+        print(f"Complex density: {complex_density}")
+    except Exception as e:
+        print(f"Error with complex density: {e}")
+
+    try:
+        complex_diffusivity = thermal_diffusivity_by_heat_conductivity(k_symbolic, rho_symbolic + sp.I, c_p_symbolic)
+        print(f"Complex diffusivity: {complex_diffusivity}")
+    except Exception as e:
+        print(f"Error with complex diffusivity: {e}")
diff --git a/src/materials/tests.py b/src/materials/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..40bdbc08f555522de9412e9def731d9ed6692f08
--- /dev/null
+++ b/src/materials/tests.py
@@ -0,0 +1,326 @@
+import pytest
+import numpy as np
+import sympy as sp
+from pystencils.types.quick import Arr
+
+from src.materials.alloys.Alloy import Alloy
+from src.materials.alloys.SS316L.SS316L import create_SS316L
+from src.materials.assignment_converter import type_mapping, assignment_converter
+from src.materials.elements import Fe, Cr, Mn, Ni
+from src.materials.interpolators import interpolate_property, interpolate_lookup, interpolate_equidistant, \
+    test_equidistant
+from src.materials.models import density_by_thermal_expansion, thermal_diffusivity_by_heat_conductivity
+from src.materials.typedefs import MaterialProperty, Assignment
+
+
+def test_alloy_creation():
+    # Test creating an alloy with valid elemental composition and phase transition temperatures
+    alloy = Alloy(elements=[Fe, Cr, Mn, Ni], composition=[0.7, 0.2, 0.05, 0.05], temperature_solidus=1700, temperature_liquidus=1800)
+    assert np.allclose(alloy.composition, [0.7, 0.2, 0.05, 0.05])
+    assert alloy.temperature_solidus == 1700
+    assert alloy.temperature_liquidus == 1800
+
+    # Test creating an alloy with invalid elemental composition
+    with pytest.raises(ValueError):
+        Alloy(elements=[Fe, Cr], composition=[0.6, 0.5], temperature_solidus=1700, temperature_liquidus=1800)
+
+    # Test creating an alloy with invalid phase transition temperatures
+    with pytest.raises(ValueError):
+        Alloy(elements=[Fe, Cr, Mn, Ni], composition=[0.7, 0.2, 0.05, 0.05], temperature_solidus=1900, temperature_liquidus=1800)
+
+def test_create_SS316L():
+    # Test creating SS316L alloy with a float temperature input
+    alloy = create_SS316L(1400.0)
+    assert isinstance(alloy.density, MaterialProperty)
+    assert isinstance(alloy.heat_capacity, MaterialProperty)
+    assert isinstance(alloy.heat_conductivity, MaterialProperty)
+    assert isinstance(alloy.thermal_diffusivity, MaterialProperty)
+
+    # Test creating SS316L alloy with a symbolic temperature input
+    T = sp.Symbol('T')
+    alloy = create_SS316L(T)
+    assert isinstance(alloy.density, MaterialProperty)
+    assert isinstance(alloy.heat_capacity, MaterialProperty)
+    assert isinstance(alloy.heat_conductivity, MaterialProperty)
+    assert isinstance(alloy.thermal_diffusivity, MaterialProperty)
+
+    # Test creating SS316L alloy with different combinations of property calculation methods
+    alloy = create_SS316L(1400.0)
+    alloy.density = alloy.density.expr
+    alloy.heat_capacity = alloy.heat_capacity.evalf(T, 1400.0)
+    assert isinstance(alloy.density, sp.Expr)
+    assert isinstance(alloy.heat_capacity, float)
+
+def test_interpolate_property():
+    # Test interpolating properties with float temperature inputs
+    T_symbol = sp.Symbol('T')  # Define a symbolic variable
+    T_value = 1400.0
+    temp_array = np.array([1300.0, 1400.0, 1500.0])
+    prop_array = np.array([100.0, 200.0, 300.0])
+    result = interpolate_property(T_symbol, temp_array, prop_array)
+    assert isinstance(result, MaterialProperty)
+    assert np.isclose(result.evalf(T_symbol, T_value), 200.0)
+
+    # Test interpolating properties with symbolic temperature inputs
+    result = interpolate_property(T_symbol, temp_array, prop_array)
+    assert isinstance(result, MaterialProperty)
+    assert np.isclose(result.evalf(T_symbol, T_value), 200.0)
+
+    # Test interpolating properties with different combinations of temperature and property arrays
+    temp_array = [1300.0, 1400.0, 1500.0]
+    prop_array = [100.0, 200.0, 300.0]
+    result = interpolate_property(T_symbol, temp_array, prop_array)
+    assert isinstance(result, MaterialProperty)
+    assert np.isclose(result.evalf(T_symbol, T_value), 200.0)
+
+    temp_array = tuple([1300.0, 1400.0, 1500.0])
+    prop_array = tuple([100.0, 200.0, 300.0])
+    result = interpolate_property(T_symbol, temp_array, prop_array)
+    assert isinstance(result, MaterialProperty)
+    assert np.isclose(result.evalf(T_symbol, T_value), 200.0)
+
+    # Test with ascending and descending order arrays
+    temp_array_asc = np.array([1300.0, 1400.0, 1500.0])
+    prop_array_asc = np.array([100.0, 200.0, 300.0])
+    result_asc = interpolate_property(T_symbol, temp_array_asc, prop_array_asc)
+    assert isinstance(result_asc, MaterialProperty)
+    assert np.isclose(result_asc.evalf(T_symbol, T_value), 200.0)
+
+    temp_array_desc = np.array([1500.0, 1400.0, 1300.0])
+    prop_array_desc = np.array([300.0, 200.0, 100.0])
+    result_desc = interpolate_property(T_symbol, temp_array_desc, prop_array_desc)
+    assert isinstance(result_desc, MaterialProperty)
+    assert np.isclose(result_desc.evalf(T_symbol, T_value), 200.0)
+
+    # Test with different temp_array_limit values
+    result_limit_6 = interpolate_property(T_symbol, temp_array_asc, prop_array_asc, temp_array_limit=6)
+    assert isinstance(result_limit_6, MaterialProperty)
+    assert np.isclose(result_limit_6.evalf(T_symbol, T_value), 200.0)
+
+    result_limit_3 = interpolate_property(T_symbol, temp_array_asc, prop_array_asc, temp_array_limit=3)
+    assert isinstance(result_limit_3, MaterialProperty)
+    assert np.isclose(result_limit_3.evalf(T_symbol, T_value), 200.0)
+
+    # Test with force_lookup True and False
+    result_force_lookup_true = interpolate_property(T_symbol, temp_array_asc, prop_array_asc, force_lookup=True)
+    assert isinstance(result_force_lookup_true, MaterialProperty)
+    assert np.isclose(result_force_lookup_true.evalf(T_symbol, T_value), 200.0)
+
+    result_force_lookup_false = interpolate_property(T_symbol, temp_array_asc, prop_array_asc, force_lookup=False)
+    assert isinstance(result_force_lookup_false, MaterialProperty)
+    assert np.isclose(result_force_lookup_false.evalf(T_symbol, T_value), 200.0)
+
+def test_interpolate_lookup():
+    # Define a symbolic variable
+    T_symbol = sp.Symbol('T')
+
+    # Test lookup interpolation with float temperature inputs
+    T_value = 1400.0
+    temp_array = np.array([1300.0, 1400.0, 1500.0])
+    prop_array = np.array([100.0, 200.0, 300.0])
+    result = interpolate_lookup(T_symbol, temp_array, prop_array)
+    assert isinstance(result, MaterialProperty)
+    assert np.isclose(result.evalf(T_symbol, T_value), 200.0)
+
+    # Test lookup interpolation with symbolic temperature inputs
+    result = interpolate_lookup(T_symbol, temp_array, prop_array)
+    assert isinstance(result, MaterialProperty)
+    assert np.isclose(result.evalf(T_symbol, T_value), 200.0)
+
+    # Test with different combinations of temperature and property arrays
+    temp_array = [1300.0, 1400.0, 1500.0]
+    prop_array = [100.0, 200.0, 300.0]
+    result = interpolate_lookup(T_symbol, temp_array, prop_array)
+    assert isinstance(result, MaterialProperty)
+    assert np.isclose(result.evalf(T_symbol, T_value), 200.0)
+
+    temp_array = tuple([1300.0, 1400.0, 1500.0])
+    prop_array = tuple([100.0, 200.0, 300.0])
+    result = interpolate_lookup(T_symbol, temp_array, prop_array)
+    assert isinstance(result, MaterialProperty)
+    assert np.isclose(result.evalf(T_symbol, T_value), 200.0)
+
+    # Test with ascending and descending order arrays
+    temp_array_asc = np.array([1300.0, 1400.0, 1500.0])
+    prop_array_asc = np.array([100.0, 200.0, 300.0])
+    result_asc = interpolate_lookup(T_symbol, temp_array_asc, prop_array_asc)
+    assert isinstance(result_asc, MaterialProperty)
+    assert np.isclose(result_asc.evalf(T_symbol, T_value), 200.0)
+
+    temp_array_desc = np.array([1500.0, 1400.0, 1300.0])
+    prop_array_desc = np.array([300.0, 200.0, 100.0])
+    result_desc = interpolate_lookup(T_symbol, temp_array_desc, prop_array_desc)
+    assert isinstance(result_desc, MaterialProperty)
+    assert np.isclose(result_desc.evalf(T_symbol, T_value), 200.0)
+
+def test_interpolate_equidistant():
+    # Define a symbolic variable
+    T_symbol = sp.Symbol('T')
+
+    # Test equidistant interpolation with float temperature inputs
+    T_value = 1400.0
+    temp_base = 1300.0
+    temp_incr = 100.0
+    prop_array = np.array([100.0, 200.0, 300.0])
+    result = interpolate_equidistant(T_value, temp_base, temp_incr, prop_array)
+    assert isinstance(result, MaterialProperty)
+    assert np.isclose(result.evalf(T_symbol, T_value), 200.0)
+
+    # Test equidistant interpolation with symbolic temperature inputs
+    result = interpolate_equidistant(T_symbol, temp_base, temp_incr, prop_array)
+    assert isinstance(result, MaterialProperty)
+    assert np.isclose(result.evalf(T_symbol, T_value), 200.0)
+
+    # Test with different combinations of temperature and property arrays
+    temp_base = 1300.0
+    temp_incr = 100.0
+    prop_array = [100.0, 200.0, 300.0]
+    result = interpolate_equidistant(T_symbol, temp_base, temp_incr, prop_array)
+    assert isinstance(result, MaterialProperty)
+    assert np.isclose(result.evalf(T_symbol, T_value), 200.0)
+
+    temp_base = 1300.0
+    temp_incr = 100.0
+    prop_array = tuple([100.0, 200.0, 300.0])
+    result = interpolate_equidistant(T_symbol, temp_base, temp_incr, prop_array)
+    assert isinstance(result, MaterialProperty)
+    assert np.isclose(result.evalf(T_symbol, T_value), 200.0)
+
+    # Test with ascending and descending order arrays
+    temp_base = 1300.0
+    temp_incr = 100.0
+    prop_array_asc = np.array([100.0, 200.0, 300.0])
+    result_asc = interpolate_equidistant(T_symbol, temp_base, temp_incr, prop_array_asc)
+    assert isinstance(result_asc, MaterialProperty)
+    assert np.isclose(result_asc.evalf(T_symbol, T_value), 200.0)
+
+    temp_base = 1300.0
+    temp_incr = 100.0
+    prop_array_desc = np.array([300.0, 200.0, 100.0])
+    result_desc = interpolate_equidistant(T_symbol, temp_base, temp_incr, prop_array_desc)
+    assert isinstance(result_desc, MaterialProperty)
+    assert np.isclose(result_desc.evalf(T_symbol, T_value), 200.0)
+
+# Define the temp fixture
+@pytest.fixture
+def temp():
+    return np.array([100.0, 200.0, 300.0, 400.0, 500.0])
+
+def test_test_equidistant(temp):
+    # Test with equidistant temperature array
+    assert test_equidistant(temp) == 100.0
+
+    # Test with non-equidistant temperature array
+    temp_non_equidistant = np.array([100.0, 150.0, 300.0, 450.0, 500.0])
+    assert test_equidistant(temp_non_equidistant) == 0.0
+
+def test_density_by_thermal_expansion():
+    # Test calculating density with float temperature and thermal expansion coefficient inputs
+    T = 1400.0
+    T_ref = 1000.0
+    rho_ref = 8000.0
+    alpha = 1e-5
+    result = density_by_thermal_expansion(T, T_ref, rho_ref, alpha)
+    assert np.isclose(result, 7904.76)
+
+    # Test calculating density with symbolic temperature and thermal expansion coefficient inputs
+    T = sp.Symbol('T')
+    result = density_by_thermal_expansion(T, T_ref, rho_ref, alpha)
+    assert isinstance(result, sp.Expr)
+    assert np.isclose(result.subs(T, 1400.0), 7904.76)
+
+    # Test calculating density with numpy array temperature and thermal expansion coefficient inputs
+    T = np.array([1300.0, 1400.0, 1500.0])
+    result = density_by_thermal_expansion(T, T_ref, rho_ref, alpha)
+    assert isinstance(result, np.ndarray)
+    assert np.allclose(result, [7928.43, 7904.76, 7881.19])
+
+def test_thermal_diffusivity_by_heat_conductivity():
+    # Test calculating thermal diffusivity with float heat conductivity, density, and heat capacity inputs
+    k = 50.0
+    rho = 8000.0
+    c_p = 500.0
+    result = thermal_diffusivity_by_heat_conductivity(k, rho, c_p)
+    assert np.isclose(result, 1.25e-5)
+
+    # Test calculating thermal diffusivity with symbolic heat conductivity, density, and heat capacity inputs
+    k = sp.Symbol('k')
+    rho = sp.Symbol('rho')
+    c_p = sp.Symbol('c_p')
+    result = thermal_diffusivity_by_heat_conductivity(k, rho, c_p)
+    assert isinstance(result, sp.Expr)
+    assert result == k / (rho * c_p)
+
+    # Test calculating thermal diffusivity with numpy array heat conductivity, density, and heat capacity inputs
+    k = np.array([50.0, 60.0, 70.0])
+    rho = np.array([8000.0, 8100.0, 8200.0])
+    c_p = np.array([500.0, 510.0, 520.0])
+    result = thermal_diffusivity_by_heat_conductivity(k, rho, c_p)
+    assert isinstance(result, np.ndarray)
+    assert np.allclose(result, [1.25e-5, 1.45243282e-5, 1.64165103e-5])
+
+def test_alloy_single_element():
+    # Test creating an alloy with a single element
+    alloy = Alloy(elements=[Fe], composition=[1.0], temperature_solidus=1800, temperature_liquidus=1900)
+    assert len(alloy.elements) == 1
+    assert alloy.composition == [1.0]
+
+def test_alloy_property_modification():
+    # Test accessing and modifying individual alloy properties
+    alloy = create_SS316L(1400.0)
+    assert isinstance(alloy.density, MaterialProperty)
+    # Set the density to a MaterialProperty instance with a constant expression
+    alloy.density = MaterialProperty(expr=sp.Float(8000.0))
+    assert alloy.density.expr == sp.Float(8000.0)
+
+def test_create_SS316L_invalid_temperature():
+    # Test creating SS316L alloy with invalid temperature inputs
+    with pytest.raises(ValueError):
+        create_SS316L(-100.0)  # Negative temperature
+    with pytest.raises(ValueError):
+        create_SS316L(3500.0)  # Temperature outside valid range
+
+def test_density_by_thermal_expansion_invalid_inputs():
+    # Test calculating density with invalid temperature or thermal expansion coefficient inputs
+    with pytest.raises(ValueError):
+        density_by_thermal_expansion(-100.0, 1000.0, 8000.0, 1e-5)
+    with pytest.raises(ValueError):
+        density_by_thermal_expansion(1000.0, -1000.0, 8000.0, 1e-5)
+    with pytest.raises(ValueError):
+        density_by_thermal_expansion(1000.0, 1000.0, -8000.0, 1e-5)
+    with pytest.raises(ValueError):
+        density_by_thermal_expansion(1000.0, 1000.0, 8000.0, -1e-5)
+    with pytest.raises(ValueError):
+        density_by_thermal_expansion(np.array([-100.0, 1000.0]), 1000.0, 8000.0, 1e-5)
+
+def test_thermal_diffusivity_invalid_inputs():
+    # Test calculating thermal diffusivity with invalid heat conductivity, density, or heat capacity inputs
+    with pytest.raises(ValueError):
+        thermal_diffusivity_by_heat_conductivity(-10.0, 8000.0, 500.0)  # Negative heat conductivity
+    with pytest.raises(ValueError):
+        thermal_diffusivity_by_heat_conductivity(50.0, -8000.0, 500.0)  # Negative density
+    with pytest.raises(ValueError):
+        thermal_diffusivity_by_heat_conductivity(50.0, 8000.0, -500.0)  # Negative heat capacity
+
+def test_type_mapping_unsupported():
+    # Test mapping unsupported type strings
+    with pytest.raises(ValueError):
+        type_mapping("unsupported_type", 1)
+
+# Additional test cases
+def test_type_mapping():
+    assert isinstance(type_mapping("double[]", 5), Arr)
+    assert isinstance(type_mapping("float[]", 3), Arr)
+    assert type_mapping("double", 1) == np.dtype('float64')
+    assert type_mapping("float", 1) == np.dtype('float32')
+    assert type_mapping("int", 1) == np.dtype('int32')
+    assert type_mapping("bool", 1) == np.dtype('bool')
+
+    with pytest.raises(ValueError):
+        type_mapping("unsupported_type", 1)
+
+def test_assignment_converter_invalid():
+    # Test converting assignments with invalid or missing attributes
+    invalid_assignment = Assignment(lhs=None, rhs=None, lhs_type=None)
+    with pytest.raises(ValueError, match="Invalid assignment: lhs, rhs, and lhs_type must not be None"):
+        assignment_converter([invalid_assignment])
diff --git a/src/materials/typedefs.py b/src/materials/typedefs.py
index 7ba5ff70446c44c87c2cbf31ca62c22eae29ea84..a51c4f26202fec27775c1e3a93621f335c59f5ab 100644
--- a/src/materials/typedefs.py
+++ b/src/materials/typedefs.py
@@ -43,7 +43,7 @@ class Assignment:
 
 # Type aliases for various data types used in the project
 ArrayTypes = Union[np.ndarray, List, Tuple]  # Numerical values can be represented as numpy arrays, lists, or tuples
-
+# def evalf(self, symbol: sp.Symbol, temperature: Union[sp.Symbol, float, ArrayTypes]) -> Union[float, np.ndarray]:
 
 @dataclass
 class MaterialProperty:
@@ -78,10 +78,8 @@ class MaterialProperty:
         if not self.expr.free_symbols:
             return float(self.expr)
 
-        # If temperature is a numpy array, list, or tuple, evaluate the property for each temperature
+        # If temperature is a numpy array, list, or tuple (ArrayTypes), evaluate the property for each temperature
         if isinstance(temperature, get_args(ArrayTypes)):
-            if isinstance(temperature, np.ndarray):
-                temperature = temperature.astype(float)  # Ensure all values are Python floats
             return np.array([self.evalf(symbol, t) for t in temperature])
 
         # Convert any numpy scalar to Python float
@@ -101,8 +99,12 @@ class MaterialProperty:
 
         # Evaluate the material property with the substitutions
         result = sp.N(self.expr.subs(substitutions))
-        return float(result)
-
+        # return float(result)
+        # Try to convert the result to float if possible
+        try:
+            return float(result)
+        except TypeError:
+            return result  # Return the symbolic expression if it cannot be converted to a float
 
 # Type alias for properties that can be either constant floats or MaterialProperty instances
 PropertyTypes = Union[float, MaterialProperty]
@@ -195,3 +197,59 @@ if __name__ == '__main__':
     # Test 10: Property with no symbolic dependencies
     mp7 = MaterialProperty(sp.Float(1500.))
     print(f"Test 10 - Property with no symbolic dependencies: {mp7.evalf(T, 200.)}")  # Expected output: 1500.0
+
+    # Test 11: Piecewise function
+    mp8 = MaterialProperty(sp.Piecewise((100, T < 0), (200, T >= 100), (T, True)))
+    print(f"Test 11a - Piecewise function at T=-10: {mp8.evalf(T, -10)}")  # Expected: 100
+    print(f"Test 11b - Piecewise function at T=50: {mp8.evalf(T, 50)}")   # Expected: 50
+    print(f"Test 11c - Piecewise function at T=150: {mp8.evalf(T, 150)}") # Expected: 200
+
+    # Test 12: Complex expression with trigonometric functions
+    mp9 = MaterialProperty(100 * sp.sin(T) + 50 * sp.cos(T))
+    print(f"Test 12a - Complex expression at T=0: {mp9.evalf(T, 0)}")       # Expected: 50
+    print(f"Test 12b - Complex expression at T=pi/2: {mp9.evalf(T, np.pi/2)}") # Expected: 100
+
+    # Test 13: Expression with logarithmic and exponential functions
+    mp10 = MaterialProperty(10 * sp.log(T + 1) + 5 * sp.exp(T/100))
+    print(f"Test 13 - Log and exp expression at T=10: {mp10.evalf(T, 10)}") # Expected: ~28.19
+
+    # Test 14: Property with multiple variables
+    T2 = sp.Symbol('T2')
+    mp11 = MaterialProperty(T * T2)
+    res = mp11.evalf(T, 10)
+    print(f"Test 14 - Multi-variable property: {res}")  # This should raise an error or return a symbolic expression
+    assert isinstance(res, sp.Expr)
+    assert sp.Eq(res, 10 * T2)
+
+    # Test 15: Handling very large and very small numbers
+    mp12 = MaterialProperty(1e20 * T + 1e-20)
+    print(f"Test 15a - Large numbers: {mp12.evalf(T, 1e5)}")
+    print(f"Test 15b - Small numbers: {mp12.evalf(T, 1e-5)}")
+
+    # Test 16: Property with rational functions
+    mp13 = MaterialProperty((T**2 + 1) / (T + 2))
+    print(f"Test 16 - Rational function at T=3: {mp13.evalf(T, 3)}")
+
+    # Test 17: Handling undefined values (division by zero)
+    mp14 = MaterialProperty(1 / (T - 1))
+    try:
+        res = mp14.evalf(T, 1)
+        print(f"Test 17 - Division by zero at T=1: {res}")  # "zoo" in SymPy represents complex infinity
+    except ZeroDivisionError:
+        print("Test 17 - Division by zero at T=1: ZeroDivisionError raised as expected")
+
+    # Test 18: Property with absolute value
+    mp15 = MaterialProperty(sp.Abs(T - 50))
+    print(f"Test 18a - Absolute value at T=30: {mp15.evalf(T, 30)}")
+    print(f"Test 18b - Absolute value at T=70: {mp15.evalf(T, 70)}")
+
+    # Test 19: Property with floor and ceiling functions
+    mp16 = MaterialProperty(sp.floor(T/10) + sp.ceiling(T/10))
+    print(f"Test 19 - Floor and ceiling at T=25: {mp16.evalf(T, 25)}")
+
+    # Test 20: Handling complex numbers
+    mp17 = MaterialProperty(sp.sqrt(T))
+    print(f"Test 20a - Square root at T=4: {mp17.evalf(T, 4)}")
+    res = mp17.evalf(T, -1)
+    print(f"Test 20b - Square root at T=-1: {res}")
+    assert isinstance(res, sp.Expr)