diff --git a/foamPython/.gitignore b/foamPython/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..90c2c3f6ad1baf6107bbb918926b9ad4661999c1
--- /dev/null
+++ b/foamPython/.gitignore
@@ -0,0 +1,15 @@
+gignore/
+
+*__pycache__
+
+# Results:
+*.[0-9]
+
+# Cython stuff:
+*.c
+*build
+*.so
+
+# Ignore everything in the constant folder, but still keep it:
+constant/*
+!constant/.a
diff --git a/foamPython/README.md b/foamPython/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..fabcce20c60e690010184217b398ecc50e1416ab
--- /dev/null
+++ b/foamPython/README.md
@@ -0,0 +1,30 @@
+Author: Robert Anderluh, 2021
+
+
+Python3 code (used Python 3.6.9 at the time of writing) which mimics the basic classes and functionality of OpenFOAM.
+
+The source code is present in the src/ folder.
+
+
+Implemented laplacianFoam, simpleFoam and icoFoam.
+
+
+scriptProfileCode runs a profiler of the code.
+
+scriptCompareSimple copies the results to a folder with the equivalent simulation in OpenFOAM, for comparison in ParaView or similar.
+
+scriptSetCython does a compilation of the most computationaly intensive functions (at time of writing Amul in fvSolution and Gauss Seidel solver).
+
+scriptUnsetCython reverts the code to a standard python / no Cython implementation.
+
+
+Using the code:
+
+Examples of Allrun and Allclean scripts are in the tutorials/ directory, with examples for different solvers.
+
+Before running Allrun, make sure you have sourced OpenFOAM (a fork which supports blockMeshDict in the system/ directory).
+
+All of the parameters required for running the case are defined in caseSetup.py.
+
+Meshes should be in the OpenFOAM format and in the same location as with OpenFOAM - constant/polyMesh inside the case directory.
+They can be generated using blockMesh, be inserting an existing polyMesh folder from an existing OpenFOAM case, or by using other OpenFOAM mesh generation utilities.
diff --git a/foamPython/functions.py b/foamPython/functions.py
new file mode 100644
index 0000000000000000000000000000000000000000..69b0604c5b413b235cabeb252b38a9e02df6b4d0
--- /dev/null
+++ b/foamPython/functions.py
@@ -0,0 +1,41 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    General useful functions
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import os
+import psutil
+import numpy as np
+from src.OpenFOAM.fields.include import *
+
+def printMemoryUsageInMB():
+	print("\nMemory usage is: " + str(psutil.Process(os.getpid()).memory_info().rss / 1024 ** 2) + ' MB\n')
+
+
+# def weightedAverage(field, weightField):
+	
+	# if (np.size(field) != np.size(weightField)):
+		# raise RuntimeError("Field sizes in weightedAverage function are not the same!")
+	
+	# weightFieldAverage = np.average(weightField)
+	
+	# result = np.average(field * weightField / weightFieldAverage)
+	
+	# return result
+
+
+
+	
diff --git a/foamPython/icoFoam.py b/foamPython/icoFoam.py
new file mode 100644
index 0000000000000000000000000000000000000000..fbc2d2bb02d5217ae0bb029b78f6ed92c47eebb0
--- /dev/null
+++ b/foamPython/icoFoam.py
@@ -0,0 +1,173 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Incompressible transient fluid flow solver based on the PISO algorithm,
+    with laminar flow only
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import time as timeModule
+import os
+
+import functions as fn
+
+from src.finiteVolume.fvMesh.fvMesh import *
+from src.finiteVolume.fvMatrices.include import *
+from src.OpenFOAM.fields.include import *
+from src.finiteVolume.cfdTools.include import *
+
+# Case setup file
+
+caseSetup = "caseSetup.py"
+
+exec(open(caseSetup).read())
+caseSetupModifyTime = os.path.getmtime(caseSetup)
+
+
+startClockTime = timeModule.perf_counter()
+
+
+# Read in the mesh
+mesh = fvMesh.constructFromPolyMeshFolder("constant/polyMesh")
+
+
+clockTime = round(timeModule.perf_counter() - startClockTime, 2)
+print("Mesh creation lasted for", clockTime, 's\n')
+
+
+# Initialize fields
+U = volVectorField.initialize("U", mesh, boundaryDefinition_U, internalField_U)
+p = volScalarField.initialize("p", mesh, boundaryDefinition_p, internalField_p)
+phi = surfaceScalarField.flux(U)
+
+
+cumulativeContErr = 0.0
+time = startTime
+U.write(round(time, 10)) # Write 0 time
+p.write(round(time, 10)) # Write 0 time
+while (endTime - time > 1e-10):
+	
+	# Reread caseSetup if it is modified
+	if ((os.path.getmtime(caseSetup) != caseSetupModifyTime) and		   \
+		runTimeModifiable):
+		
+		print("Rereading case setup file.")
+		exec(open(caseSetup).read())
+		caseSetupModifyTime = os.path.getmtime(caseSetup)
+		
+	# Increment time
+	if (time + deltaT > endTime):
+		deltaT = endTime - time
+	time += deltaT
+	
+	print("\nTime =", round(time, 10))
+	
+	"""---------------------------- U equation ------------------------------"""
+	
+	# Assemble the U matrix contributions
+	ddtU = fvm.construct(U, 'ddt', ddtScheme, [deltaT])
+	divUU = fvm.construct(U, 'div', divUScheme, [phi])
+	divNuGradU = fvm.construct(U, 'laplacian', laplacianScheme, [nu])
+	gradP = fvc.construct(U, 'grad', gradPScheme, [p])
+	
+	# U matrix
+	UEqn = (ddtU + divUU) - divNuGradU
+
+	# Solve UEqn
+	(UEqn == (-gradP) ).solve(fvSolution_U)
+	
+	# Set U boundary values after solving
+	U.setBoundaryValues(boundaryDefinition_U)
+	
+	nCorrPISO = 0
+	
+	# PISO loop
+	while (nCorrPISO < PISO['nCorrectors']):
+
+		"""------------------------------------------------------------------"""
+		
+		rAU = UEqn.rA()
+		HU = UEqn.H()
+		HbyA = rAU * HU
+
+		constrainHbyA(HbyA, U)
+		# print("HbyA.data.cellValues_[0][0] =", HbyA.data.cellValues_[0][0])
+		# print("U.data.cellValues_[0][0] =", U.data.cellValues_[0][0])
+		# print("UEqn.data.field_.data.cellValues_[0][0] =", UEqn.data.field_.data.cellValues_[0][0])
+		"""-------------------------- p equation ----------------------------"""
+		
+		# Assemble the p matrix contributions
+		divrAUGradp = fvm.construct(p, 'laplacian', laplacianScheme, [rAU])
+		divrAUHU = fvc.construct(p, 'div', divHbyAScheme, [HbyA])
+		
+		# p matrix
+		pEqn = (divrAUGradp == divrAUHU)
+		
+		# Set reference values for the p field	
+		p.setRefValue(fvSolution_p)
+		
+		# Solve pEqn
+		pEqn.solve(fvSolution_p)
+		
+		# Set p boundary values after solving
+		p.setBoundaryValues(boundaryDefinition_p)
+
+		"""------------------------------------------------------------------"""
+		
+		
+		"""----------------------- Flux correction --------------------------"""
+		
+		phiHbyA = surfaceScalarField.flux(HbyA)
+		
+		gradPVolField = volVectorField.grad(p)
+		gradPbyA = rAU * gradPVolField
+		phigradPbyA = surfaceScalarField.flux(gradPbyA) # Unit = [m3/s]
+		
+		phi = phiHbyA - phigradPbyA # Unit = [m3/s]
+		
+		"""------------------------------------------------------------------"""
+		
+		"""--------------------- Momentum correction ------------------------"""
+		
+		# U = HbyA - gradPbyA
+		U = HbyA - rAU * volVectorField.grad(p)
+		
+		U.updateBoundaryDefinition(boundaryDefinition_U)
+		U.updateFieldName("U")
+		U.setBoundaryValues(boundaryDefinition_U)
+		
+		UEqn.data.psi_ = U
+		
+		nCorrPISO += 1
+	
+	exec(continuityErrs)
+	
+	"""----------------------------------------------------------------------"""
+	
+	if ((time + 1e-11) % writeTime < 1e-10):
+		
+		# Write the result into a file
+		U.write(round(time, 10))
+		p.write(round(time, 10))
+		phi.updateFieldName("phi")
+		phi.write(round(time, 10))
+	
+	# Print out the execution time
+	clockTime = round(timeModule.perf_counter() - startClockTime, 2)
+	print("\nTotal execution time =", clockTime, 's\n')
+	fn.printMemoryUsageInMB()
+
+print("\nSimulation ended at time", round(time, 10))
+
+
diff --git a/foamPython/laplacianFoam.py b/foamPython/laplacianFoam.py
new file mode 100644
index 0000000000000000000000000000000000000000..95afe1dcf06b2389b80282df78253161e54a70b5
--- /dev/null
+++ b/foamPython/laplacianFoam.py
@@ -0,0 +1,93 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Transient Laplace's equation solver
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import time as timeModule
+import os
+
+import functions as fn
+
+from src.finiteVolume.fvMesh.fvMesh import *
+from src.finiteVolume.fvMatrices.include import *
+from src.OpenFOAM.fields.include import *
+
+# Case setup file
+
+caseSetup = "caseSetup.py"
+
+exec(open(caseSetup).read())
+caseSetupModifyTime = os.path.getmtime(caseSetup)
+
+startClockTime = timeModule.perf_counter()
+
+
+# Read in the mesh
+mesh = fvMesh.constructFromPolyMeshFolder("constant/polyMesh")
+
+
+clockTime = round(timeModule.perf_counter() - startClockTime, 2)
+print("Mesh creation lasted for", clockTime, 's\n')
+
+
+# Initialize field T
+T = volScalarField.initialize("T", mesh, boundaryDefinition_T, internalField_T)
+
+
+time = startTime
+T.write(round(time, 10)) # Write 0 time
+while (endTime - time > 1e-10):
+	
+	# Reread caseSetup if it is modified
+	if ((os.path.getmtime(caseSetup) != caseSetupModifyTime) and		   \
+		runTimeModifiable):
+		
+		print("Rereading case setup file.")
+		exec(open(caseSetup).read())
+		caseSetupModifyTime = os.path.getmtime(caseSetup)
+		
+	# Increment time
+	if (time + deltaT > endTime):
+		deltaT = endTime - time
+	time += deltaT
+	
+	print("\nTime =", round(time, 10))
+	
+	# Assemble new ddt matrix contributions
+	ddtMatrix = fvm.construct(T, 'ddt', ddtScheme, [deltaT])
+	# Assemble the Laplacian matrix contributions
+	laplacianMatrix = fvm.construct(T, 'laplacian', laplacianScheme, [DT])
+	
+	# The actual matrix to solve for
+	matrix = ddtMatrix - laplacianMatrix
+	
+	matrix.solve(fvSolution_T)
+	
+	gradT = volVectorField.grad(T)
+	
+	if ((time + 1e-11) % writeTime < 1e-10):
+		
+		# Write the result into a file
+		T.write(round(time, 10))
+		gradT.write(round(time, 10))
+	
+	# Print out the execution time
+	clockTime = round(timeModule.perf_counter() - startClockTime, 2)
+	print("\nTotal execution time =", clockTime, 's\n')
+
+print("\nSimulation ended at time", round(time, 10))
+
+
diff --git a/foamPython/scriptCompareCython.py b/foamPython/scriptCompareCython.py
new file mode 100644
index 0000000000000000000000000000000000000000..13a534394cbe6c1d6b1c4fa010ddeb8bc62dc178
--- /dev/null
+++ b/foamPython/scriptCompareCython.py
@@ -0,0 +1,69 @@
+import numpy as np
+import timeit
+import time as timeModule
+import os
+
+solver = "simpleFoam.py"
+
+noRuns = 100
+
+resultsFile = "gignore/cythonComparisonResults.dat"
+
+
+# Calculate average times without Cython
+
+os.system("sh scriptUnsetCython.sh > /dev/null 2>&1")
+
+noCyTimes = np.empty(0, dtype=float)
+
+for i in range(noRuns):
+	
+	os.system("rm -r [1-9]* > /dev/null 2>&1 ; rm -r 0.[0-9]* > /dev/null 2>&1")
+	
+	start = timeModule.perf_counter()
+	# os.system("python3 " + solver)
+	os.system("python3 " + solver + "> /dev/null 2>&1")
+	end = timeModule.perf_counter()
+	
+	noCyTimes = np.append(noCyTimes, (end - start))
+	
+	print("No Cython run", i+1, "done.")
+
+
+
+# Calculate average times with Cython
+
+os.system("sh scriptSetCython.sh > /dev/null 2>&1")
+
+CyTimes = np.empty(0, dtype=float)
+
+for i in range(noRuns):
+	
+	os.system("rm -r [1-9]* > /dev/null 2>&1 ; rm -r 0.[0-9]* > /dev/null 2>&1")
+	
+	start = timeModule.perf_counter()
+	os.system("python3 " + solver + "> /dev/null 2>&1")
+	end = timeModule.perf_counter()
+	
+	CyTimes = np.append(CyTimes, (end - start))
+	
+	print("Cython run", i+1, "done.")
+
+print("Cython speedup =", np.average(noCyTimes) / np.average(CyTimes))
+
+
+
+convergenceArray = np.empty(noRuns, dtype = float)
+
+for i in range(noRuns):
+	convergenceArray[i] = np.average(noCyTimes[:(i+1)]) / np.average(CyTimes[:(i+1)])
+
+print("Convergence history is being written to " + resultsFile)
+
+file = open(resultsFile, 'w')
+
+# file.write("#noRuns    speed-up\n")
+
+for i in range(noRuns):
+	file.write(str(i+1) + " " + str(convergenceArray[i]) + "\n")
+  
diff --git a/foamPython/scriptProfileCode.sh b/foamPython/scriptProfileCode.sh
new file mode 100755
index 0000000000000000000000000000000000000000..424cf7f2257997c2469e1263671f83c562876bf1
--- /dev/null
+++ b/foamPython/scriptProfileCode.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# Note: This script used to work when the case setup was in this folder.
+# To use, it should be added to case folders and modified accordingly
+
+python3 -m cProfile -s cumulative simpleFoam.py > profileLog
diff --git a/foamPython/scriptSetCython.sh b/foamPython/scriptSetCython.sh
new file mode 100755
index 0000000000000000000000000000000000000000..b17569f1392b6a74510a3e5a8da1e526e9080b25
--- /dev/null
+++ b/foamPython/scriptSetCython.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+scriptDir=$(pwd)
+
+
+
+
+cythonDir=src/finiteVolume/fvSolution/
+cythonFile="fvSolution"
+
+cd $cythonDir
+rm -r $cythonFile.py > /dev/null 2>&1
+cp -r "${cythonFile}_cy.pyx" $cythonFile.pyx
+python3 "setup_${cythonFile}.py" build_ext --inplace
+
+cd $scriptDir
+
+
+
+cythonDir=src/finiteVolume/fvSolution/solvers/GaussSeidel/
+cythonFile="GaussSeidel"
+
+cd $cythonDir
+rm -r $cythonFile.py > /dev/null 2>&1
+cp -r "${cythonFile}_cy.pyx" $cythonFile.pyx
+python3 "setup_${cythonFile}.py" build_ext --inplace
+
+cd $scriptDir
+
+
+# These are not a big improvement:
+
+cythonDir=src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian
+cythonFile="laplacian"
+
+cd $cythonDir
+rm -r $cythonFile.py > /dev/null 2>&1
+cp -r "${cythonFile}_cy.pyx" $cythonFile.pyx
+python3 "setup_${cythonFile}.py" build_ext --inplace
+
+cd $scriptDir
+
+
+
+cythonDir=src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian
+cythonFile="boundaryContributions"
+
+cd $cythonDir
+rm -r $cythonFile.py > /dev/null 2>&1
+cp -r "${cythonFile}_cy.pyx" $cythonFile.pyx
+python3 "setup_${cythonFile}.py" build_ext --inplace
+
+cd $scriptDir
+
+
+
+cythonDir=src/OpenFOAM/fields/volField/volVectorField
+cythonFile="volVectorField"
+
+cd $cythonDir
+rm -r $cythonFile.py > /dev/null 2>&1
+cp -r "${cythonFile}_cy.pyx" $cythonFile.pyx
+python3 "setup_${cythonFile}.py" build_ext --inplace
+
+cd $scriptDir
+
+
+
diff --git a/foamPython/scriptUnsetCython.sh b/foamPython/scriptUnsetCython.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4cbc0a15174bffb52af591347734543c59f08b3f
--- /dev/null
+++ b/foamPython/scriptUnsetCython.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+scriptDir=$(pwd)
+
+
+cythonDir=src/finiteVolume/fvSolution/
+cythonFile="fvSolution"
+
+cd $cythonDir
+rm -r build "${cythonFile}."* > /dev/null 2>&1
+cp -r "${cythonFile}_py.py" "${cythonFile}.py"
+
+cd $scriptDir
+
+
+cythonDir=src/finiteVolume/fvSolution/solvers/GaussSeidel
+cythonFile="GaussSeidel"
+
+cd $cythonDir
+rm -r build "${cythonFile}."* > /dev/null 2>&1
+cp -r "${cythonFile}_py.py" "${cythonFile}.py"
+
+cd $scriptDir
+
+
+cythonDir=src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian
+cythonFile="laplacian"
+
+cd $cythonDir
+rm -r build "${cythonFile}."* > /dev/null 2>&1
+cp -r "${cythonFile}_py.py" "${cythonFile}.py"
+
+cd $scriptDir
+
+
+cythonDir=src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian
+cythonFile="boundaryContributions"
+
+cd $cythonDir
+rm -r build "${cythonFile}."* > /dev/null 2>&1
+cp -r "${cythonFile}_py.py" "${cythonFile}.py"
+
+cd $scriptDir
+
+
+cythonDir=src/OpenFOAM/fields/volField/volVectorField
+cythonFile="volVectorField"
+
+cd $cythonDir
+rm -r build "${cythonFile}."* > /dev/null 2>&1
+cp -r "${cythonFile}_py.py" "${cythonFile}.py"
+
+cd $scriptDir
diff --git a/foamPython/simpleFoam.py b/foamPython/simpleFoam.py
new file mode 100644
index 0000000000000000000000000000000000000000..cde84cf671eae67eca5fb06ff0e872ee25c989c8
--- /dev/null
+++ b/foamPython/simpleFoam.py
@@ -0,0 +1,183 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Incompressible steady-state fluid flow solver based on the SIMPLE algorithm,
+    with laminar flow only
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import time as timeModule
+import os
+
+import functions as fn
+
+from src.finiteVolume.fvMesh.fvMesh import *
+from src.finiteVolume.fvMatrices.include import *
+from src.OpenFOAM.fields.include import *
+from src.finiteVolume.cfdTools.include import *
+
+# Case setup file
+caseSetup = "caseSetup.py"
+
+exec(open(caseSetup).read())
+caseSetupModifyTime = os.path.getmtime(caseSetup)
+
+
+startClockTime = timeModule.perf_counter()
+
+
+# Read in the mesh
+mesh = fvMesh.constructFromPolyMeshFolder("constant/polyMesh")
+
+
+clockTime = round(timeModule.perf_counter() - startClockTime, 2)
+print("Mesh creation lasted for", clockTime, 's\n')
+
+
+# Initialize fields
+U = volVectorField.initialize("U", mesh, boundaryDefinition_U, internalField_U)
+p = volScalarField.initialize("p", mesh, boundaryDefinition_p, internalField_p)
+phi = surfaceScalarField.flux(U)
+
+
+cumulativeContErr = 0.0
+time = startTime
+U.write(round(time, 10)) # Write 0 time
+p.write(round(time, 10)) # Write 0 time
+while (endTime - time > 1e-10):
+	
+	# Reread caseSetup if it is modified
+	if ((os.path.getmtime(caseSetup) != caseSetupModifyTime) and			   \
+		runTimeModifiable):
+		
+		print("Rereading case setup file.")
+		exec(open(caseSetup).read())
+		caseSetupModifyTime = os.path.getmtime(caseSetup)
+		
+	# Increment time
+	if (time + deltaT > endTime):
+		deltaT = endTime - time
+	time += deltaT
+	
+	print("\nTime =", round(time, 10))
+	
+	"""---------------------------- U equation ------------------------------"""
+	
+	# Assemble the U matrix contributions
+	divUU = fvm.construct(U, 'div', divUScheme, [phi])
+	divNuGradU = fvm.construct(U, 'laplacian', laplacianScheme, [nu])
+	gradP = fvc.construct(U, 'grad', gradPScheme, [p])
+	
+	# U matrix
+	UEqn = divUU - divNuGradU
+	
+	# Add implicit under-relaxation to the diagonal matrix coefficients
+	UEqn.relax(fvSolution_U)
+
+	# Solve UEqn
+	# (UEqn + gradP).solve(fvSolution_U)
+	(UEqn == (-gradP) ).solve(fvSolution_U)
+	
+	# Set U boundary values after solving
+	U.setBoundaryValues(boundaryDefinition_U)
+
+	"""----------------------------------------------------------------------"""
+	
+	# volScalarField: rA = 1/A * V reciprocal of U diagonal elements
+	rAU = UEqn.rA() # Unit = [s]
+	# volVectorField: HU = (b - sum_n a_n * U_n) / V 
+	HU = UEqn.H() # Unit = [m/s2]
+	# volVectorField: HbyA = rAU * HU
+	HbyA = rAU * HU # Unit = [s] * [m/s2] = [m/s]
+	
+	# Set the appropriate boundary values for HbyA
+	constrainHbyA(HbyA, U)
+	
+	"""---------------------------- p equation ------------------------------"""
+	
+	pOld = p.copy()
+	
+	# Assemble the p matrix contributions
+	divrAUGradp = fvm.construct(p, 'laplacian', laplacianScheme, [rAU])
+	divrAUHU = fvc.construct(p, 'div', divHbyAScheme, [HbyA])
+	
+	# p matrix
+	pEqn = divrAUGradp == divrAUHU
+	
+	# Set reference values for the p field	
+	p.setRefValue(fvSolution_p)
+	
+	# Solve pEqn
+	pEqn.solve(fvSolution_p)
+	
+	# Set p boundary values after solving
+	p.setBoundaryValues(boundaryDefinition_p)
+
+	"""----------------------------------------------------------------------"""
+	
+	
+	"""------------------------- Flux correction ----------------------------"""
+	
+	phiHbyA = surfaceScalarField.flux(HbyA) # Unit = [m3/s]
+	
+	gradPVolField = volVectorField.grad(p) # Unit = [m/s2]
+	# (1/ap_U) * gradP:
+	gradPbyA = rAU * gradPVolField # Unit = [s] * [m/s2] = [m/s]
+	# (1/ap_U)_f * Sf * gradP:
+	phigradPbyA = surfaceScalarField.flux(gradPbyA) # Unit = [m3/s]
+	
+	phi = phiHbyA - phigradPbyA # Unit = [m3/s]
+	
+	"""----------------------------------------------------------------------"""
+	
+	exec(continuityErrs)
+	
+	p.relax(pOld, fvSolution_p)
+
+	"""----------------------- Momentum correction --------------------------"""
+	
+	# U = HbyA - gradPbyA
+	U = HbyA - rAU * volVectorField.grad(p)
+	
+	U.updateBoundaryDefinition(boundaryDefinition_U)
+	U.updateFieldName("U")
+	U.setBoundaryValues(boundaryDefinition_U)
+	
+	"""----------------------------------------------------------------------"""
+	# # Writing auxilliary fields
+	# rAU.write(round(time, 10))
+	# HU.write(round(time, 10))
+	# HbyA.updateFieldName("HbyA")
+	# HbyA.write(round(time, 10))
+	# gradPVolField.write(round(time, 10))
+	# gradPbyA.write(round(time, 10))
+	
+	"""----------------------------------------------------------------------"""
+	
+	if ((time + 1e-11) % writeTime < 1e-10):
+		
+		p.setRefValue(fvSolution_p)
+		U.write(round(time, 10))
+		p.write(round(time, 10))
+		phi.updateFieldName("phi")
+		phi.write(round(time, 10))
+	
+	# Print out the execution time
+	clockTime = round(timeModule.perf_counter() - startClockTime, 2)
+	print("\nTotal execution time =", clockTime, 's\n')
+	fn.printMemoryUsageInMB()
+
+print("\nSimulation ended at time", round(time, 10))
+
+
diff --git a/foamPython/src/OpenFOAM/fields/fields.py b/foamPython/src/OpenFOAM/fields/fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..d739b2b1e9875bd70869f104242d8c140080478f
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/fields.py
@@ -0,0 +1,45 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Generic fields class. The foundation of the fields class hierarchy
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+import os
+import shutil
+
+from src.OpenFOAM.fields.writeHeader import HEADER
+
+
+class fields():
+	
+	fieldTypeName_ = "fieldType"
+	className_ = "fields"
+	noComponents_ = 0
+	componentsNames_ = None
+	
+	"""------------------------ General functions ---------------------------"""
+	
+	def updateBoundaryDefinition(self, boundaryDefinition):
+		
+		self.data.boundaryDefinition_ = boundaryDefinition
+		
+	def updateFieldName(self, fieldName):
+		
+		self.data.fieldName_ = fieldName
+	
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/include.py b/foamPython/src/OpenFOAM/fields/include.py
new file mode 100644
index 0000000000000000000000000000000000000000..2892c9901b1a7144cc8f7f168be2c16e87691479
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/include.py
@@ -0,0 +1,27 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Include file for the fields class hierarchy
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.OpenFOAM.fields.volField.volScalarField.volScalarField import *
+from src.OpenFOAM.fields.volField.volVectorField.volVectorField import *
+
+from src.OpenFOAM.fields.surfaceField.surfaceScalarField.surfaceScalarField import *
+from src.OpenFOAM.fields.surfaceField.surfaceVectorField.surfaceVectorField import *
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/surfaceField/surfaceField.py b/foamPython/src/OpenFOAM/fields/surfaceField/surfaceField.py
new file mode 100644
index 0000000000000000000000000000000000000000..936eae583cfe740c9a2435966a141216cda52f7d
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/surfaceField/surfaceField.py
@@ -0,0 +1,211 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Generic surface field class.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.OpenFOAM.fields.fields import *
+from src.OpenFOAM.fields.surfaceField.surfaceFieldBoundaryConditions import *
+
+
+class surfaceField(fields, surfaceFieldBoundaryConditions):
+	
+	fieldTypeName_ = "fieldType"
+	className_ = "surfaceField"
+	noComponents_ = 0
+	componentsNames_ = None
+	
+	class surfaceFieldData:
+		
+		def __init__(self):
+			
+			mesh_					= None # The mesh
+			
+			fieldName_				= None # The name of the field
+			
+			faceValues_ 			= None # Value of field in face centres
+
+			boundaryDefinition_		= None # User specified in the case setup
+										   # file
+
+	
+	"""------------------------- Constructors -------------------------------"""	
+	# Main constructor
+	def __init__															   \
+		(self, mesh, fieldName, faceValues, boundaryDefinition):
+		
+		self.data = self.surfaceFieldData()
+		
+		self.data.mesh_					= mesh
+		
+		self.data.fieldName_	 		= fieldName
+		
+		self.data.faceValues_	 		= faceValues
+
+		self.data.boundaryDefinition_	= boundaryDefinition
+	
+	# Good only for surfaceScalarField
+	def write(self, writeTime):
+		
+		# Local variable names
+		fieldTypeName = self.fieldTypeName_
+		className = self.className_
+		noComponents = self.noComponents_
+		
+		fieldName = self.data.fieldName_
+		
+		nInternalFaces = np.size(self.data.mesh_.connectivityData.neighbour_)
+		
+		# Create time directory if it does not exist
+		if os.path.exists(str(writeTime)):
+			None
+		else:
+			os.mkdir(str(writeTime))
+		
+		# Open the new file
+		file = open(str(writeTime) +'/' + fieldName, 'w')
+		
+		# Write the header and beginning of file
+		file.write(HEADER)
+		
+		file.write('    class       ' + className +';\n')
+		file.write('    location    ' + '"' + str(writeTime) + '"' + ';\n')
+		file.write('    object      ' + fieldName + ';\n}\n')
+		file.write('// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //\n\n')
+		file.write('dimensions      [0 0 0 0 0 0 0];\n\n')
+		file.write('internalField   nonuniform List<' + fieldTypeName + '>\n')
+		
+		file.write(str(nInternalFaces) + '\n(\n')
+		
+		# This function is in the individual class (volScalarField, etc.)
+		self.writeInternalFaceValues(file)
+			
+		file.write(');\n\n')
+		file.write('boundaryField\n{\n')
+		
+		self.writeBoundaryValues(file)
+			
+		file.write('}\n\n')
+		file.write('// ************************************************************************* //')
+	
+	# Good only for surfaceScalarField
+	def writeInternalFaceValues(self, file):
+		
+		nInternalFaces = np.size(self.data.mesh_.connectivityData.neighbour_)
+		noComponents = self.noComponents_
+		faceValues = self.data.faceValues_
+		
+		for faceIndex in range(nInternalFaces):
+
+			file.write(str(faceValues[0][faceIndex]) + '\n')
+	
+	# Good only for surfaceScalarField
+	def writeBoundaryValues(self, file):
+		
+		boundaryDefinition = self.data.boundaryDefinition_
+		
+		faceValues = self.data.faceValues_
+		
+		mesh = self.data.mesh_
+		
+		# Write boundary values loop
+		for boundaryName in boundaryDefinition:
+			
+			boundaryType = (boundaryDefinition[boundaryName])[0]
+			
+			file.write('\t' + boundaryName + '\n')
+			file.write('\t' + '{\n')
+			file.write(2*'\t' + 'type' + 2*'\t' +							   \
+						(boundaryDefinition[boundaryName])[0] + ';\n')
+			
+			# Assign and call the appropriate function, depending on the
+			# boundaryType
+			writeBoundaryValuesFunc = eval(									   \
+				"self.writeBoundaryValuesFunctions." + 					   \
+				boundaryType)
+				
+			writeBoundaryValuesFunc(file, boundaryName, boundaryDefinition, mesh, faceValues)
+
+			file.write('\t' + '}\n\n')
+	
+	class writeBoundaryValuesFunctions:
+		
+		def calculated(file, boundaryName, boundaryDefinition, mesh, faceValues):
+			
+			for i in mesh.connectivityData.boundary_:
+				
+				if (i[0] == boundaryName):
+					boundaryPatch = i
+					break
+			
+			nFaces = boundaryPatch[2]
+			startFace = boundaryPatch[3]
+			
+			file.write(2*'\t' + 'value' + 2*'\t' + 'nonuniform List<scalar>\n')
+			file.write(str(nFaces) + '\n')
+			file.write('(\n')
+			
+			for faceIndex in range(startFace, startFace + nFaces):
+				
+				file.write(str(faceValues[0][faceIndex]) + '\n')
+				
+			file.write(');\n')
+		
+		def empty(file, boundaryName, boundaryDefinition, mesh, faceValues):
+			
+			None
+
+	
+	"""------------------------ Defining operators --------------------------"""
+	
+	def __sub__(self, other): # - operator
+		
+		noComponents = self.noComponents_
+		
+		if (self.noComponents_ != self.noComponents_):
+			raise RuntimeError('Only surface fields with the same number of' + \
+				' components can be subtracted!')
+				
+		from src.OpenFOAM.fields.include import surfaceScalarField, surfaceVectorField
+
+		if (noComponents == 1):
+			resultClass = surfaceScalarField
+		elif (noComponents == 3):
+			resultClass = surfaceVectorField
+		
+		mesh = self.data.mesh_
+		
+		resultFaceValues = self.data.faceValues_ - other.data.faceValues_
+		
+		selfBoundaryDefinition = self.data.boundaryDefinition_
+		resultBoundaryDefinition = dict.fromkeys(selfBoundaryDefinition)
+		
+		for selfPatch in selfBoundaryDefinition:
+			 
+			if (selfBoundaryDefinition[selfPatch][0] == 'empty'):
+				resultBoundaryDefinition[selfPatch] = selfBoundaryDefinition[selfPatch]
+			else:
+				resultBoundaryDefinition[selfPatch] = 							   \
+					('calculated', None)
+
+		selfName = self.data.fieldName_
+		otherName = other.data.fieldName_
+		resultName = selfName + "-" + otherName
+		
+		return resultClass(mesh, resultName, resultFaceValues, resultBoundaryDefinition)
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/surfaceField/surfaceFieldBoundaryConditions.py b/foamPython/src/OpenFOAM/fields/surfaceField/surfaceFieldBoundaryConditions.py
new file mode 100644
index 0000000000000000000000000000000000000000..f9de914fabfcdc99ea15abc92044d9216fa3f185
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/surfaceField/surfaceFieldBoundaryConditions.py
@@ -0,0 +1,31 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    surfaceField class boundary conditions functions
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+class surfaceFieldBoundaryConditions:
+	
+	class setBoundaryValuesFunctions:
+		
+		None
+
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/surfaceField/surfaceScalarField/surfaceScalarField.py b/foamPython/src/OpenFOAM/fields/surfaceField/surfaceScalarField/surfaceScalarField.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe1aa631cbec7bab0e93dc7f8b6d2eeee6eb1e56
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/surfaceField/surfaceScalarField/surfaceScalarField.py
@@ -0,0 +1,126 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Surface scalar field class
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.OpenFOAM.fields.surfaceField.surfaceField import *
+
+
+class surfaceScalarField(surfaceField):
+	
+	fieldTypeName_ = "scalar"
+	className_ = "surfaceScalarField"
+	noComponents_ = 1
+	componentsNames_ = ("",)
+	
+	# Constructor which initializes the flux surface field using a specified
+	# volVectorField
+	@classmethod
+	def flux(self, field):
+		
+		mesh = field.data.mesh_
+		
+		fieldName = "phi" + field.data.fieldName_
+		
+		boundaryDefinitionField = field.data.boundaryDefinition_
+		boundaryDefinition = dict.fromkeys(boundaryDefinitionField)
+		
+		for patch in boundaryDefinitionField:
+			 
+			if (boundaryDefinitionField[patch][0] == 'empty'):
+				boundaryDefinition[patch] = boundaryDefinitionField[patch]
+			else:
+				boundaryDefinition[patch] = 								   \
+					('calculated', None)
+		
+		faceValues	 	= self.interpolateFlux(field)
+		
+		return self(mesh, fieldName, faceValues, boundaryDefinition)
+		
+		
+	def updateFlux(self, volVectorField):
+				
+		self.data.faceValues_ = self.interpolateFlux(volVectorField)
+	
+	
+	@classmethod
+	def interpolateFlux(self, volVectorField):
+		
+		mesh = volVectorField.data.mesh_
+		
+		noComponents = volVectorField.noComponents_
+		
+		# Total number of faces
+		nFacesTot = np.size(mesh.connectivityData.owner_)
+		# Number of internal faces
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		C = mesh.geometryData.C_
+		Cf = mesh.geometryData.Cf_
+		Sf = mesh.geometryData.Sf_
+		
+		cellValues = volVectorField.data.cellValues_
+		boundaryDefinition = volVectorField.data.boundaryDefinition_
+		
+		faceValues = np.empty((1, nFacesTot), dtype = float)
+		
+		interpVolVectorField = np.empty(noComponents, dtype = float)
+		
+		# For all internal faces
+		for faceIndex in range(nInternalFaces):
+				
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			absPf = np.linalg.norm(Cf[faceIndex] - C[ownIndex])
+			absNf = np.linalg.norm(Cf[faceIndex] - C[neiIndex])
+			
+			f = absNf / (absPf + absNf)
+			
+			# For all vector components, interpolate to face
+			for cmpt in range(noComponents):			
+				interpVolVectorField[cmpt] = 								   \
+					f * cellValues[cmpt][ownIndex] + 						   \
+					(1 - f) * cellValues[cmpt][neiIndex]
+					
+			# Result is the dot product of face area vector and the interpolated
+			# volVectorField
+			faceValues[0][faceIndex] =										   \
+				np.dot(Sf[faceIndex], interpVolVectorField)
+			
+		
+		# For all boundary faces
+		
+		volVectorFieldBoundaryValues = volVectorField.data.boundaryValues_
+		
+		for faceIndex in range(nInternalFaces, nFacesTot):
+			
+			boundaryIndex = faceIndex - nInternalFaces
+			
+			for cmpt in range(noComponents):
+				interpVolVectorField[cmpt] = volVectorFieldBoundaryValues[cmpt][boundaryIndex]
+			
+			faceValues[0][faceIndex] =										   \
+				np.dot(Sf[faceIndex], interpVolVectorField)
+			
+		return faceValues
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/surfaceField/surfaceVectorField/surfaceVectorField.py b/foamPython/src/OpenFOAM/fields/surfaceField/surfaceVectorField/surfaceVectorField.py
new file mode 100644
index 0000000000000000000000000000000000000000..d6fb6224c5a9d5ae5b43f034f62991a91ffdd258
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/surfaceField/surfaceVectorField/surfaceVectorField.py
@@ -0,0 +1,32 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Surface vector field class
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.OpenFOAM.fields.surfaceField.surfaceField import *
+
+
+class surfaceVectorField(surfaceField):
+	
+	fieldTypeName_ = "vector"
+	className_ = "surfaceVectorField"
+	noComponents_ = 3
+	componentsNames_ = ("x", "y", "z")
+	
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/volField/volField.py b/foamPython/src/OpenFOAM/fields/volField/volField.py
new file mode 100644
index 0000000000000000000000000000000000000000..a30182ca58f9b190a9454236eb70b9b1c9e7f196
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/volField/volField.py
@@ -0,0 +1,481 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Generic volume field class.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.OpenFOAM.fields.fields import *
+from src.OpenFOAM.fields.volField.volFieldBoundaryConditions import *
+
+
+class volField(fields, volFieldBoundaryConditions):
+	
+	fieldTypeName_ = "fieldType"
+	className_ = "volField"
+	noComponents_ = 0
+	componentsNames_ = None
+	
+	class volFieldData:
+		
+		def __init__(self):
+			
+			mesh_					= None # The mesh
+			
+			fieldName_				= None # The name of the field
+			cellValues_ 			= None # Value of field in cell centres
+			boundaryValues_			= None # Value of field on the boundary
+										   # patches
+			boundaryDefinition_		= None # User specified in the case setup
+										   # file
+	
+	
+	"""------------------------- Constructors -------------------------------"""	
+	# Main constructor
+	def __init__															   \
+		(self, mesh, fieldName, boundaryValues, cellValues, boundaryDefinition):
+		
+		self.data = self.volFieldData()
+		
+		self.data.mesh_					= mesh
+		self.data.fieldName_	 		= fieldName
+		self.data.cellValues_	 		= cellValues
+		self.data.boundaryValues_ 		= boundaryValues	
+		self.data.boundaryDefinition_	= boundaryDefinition
+		
+		noComponents = self.noComponents_
+
+	# Constructor which initializes the field to a specified value in all cells
+	@classmethod
+	def initialize(self, fieldName, mesh, boundaryDefinition, internalField):
+		
+		# Set cell values
+		cellValues	 	= self.setCellValuesSpecified(mesh, internalField)
+		
+		# Set boundary values
+		boundaryValues  = self.setBoundaryValuesInitial(cellValues, mesh, boundaryDefinition)
+		
+		return self(mesh, fieldName, boundaryValues, cellValues, boundaryDefinition)
+	
+	
+	"""----------------------- Initialization functions ---------------------"""
+	
+	@classmethod
+	def setCellValuesSpecified(self, mesh, internalField):
+		
+		noComponents = self.noComponents_
+		
+		nCells = mesh.read("geometryData.meshSize")
+		
+		# Each component (XYZ) is its own array
+		cellValues = np.ones((noComponents, nCells), dtype = float)
+		
+		for cmpt in range(noComponents):
+			cellValues[cmpt] *= internalField[cmpt]
+		
+		return cellValues
+	
+	@classmethod
+	def setBoundaryValuesInitial(self, cellValues, mesh, boundaryDefinition):
+		
+		# Define the required parameters and set the boundary values
+		nFacesBound = np.size(mesh.connectivityData.owner_)					   \
+					  - np.size(mesh.connectivityData.neighbour_)
+		
+		noComponents = self.noComponents_
+		
+		# Initialize the boundary array
+		boundaryValues = np.empty((noComponents, nFacesBound), dtype = float)
+		
+		# Loop over the boundaries, and assign corresponding values,
+		# depending on the boundary definition array
+		for boundaryPatch in mesh.connectivityData.boundary_:
+			boundaryName = boundaryPatch[0]
+			boundaryType = (boundaryDefinition[boundaryName])[0]
+			
+			setBoundaryValuesFunc = eval(									   \
+				"self.setBoundaryValuesFunctions." + boundaryType)
+			
+			setBoundaryValuesFunc											   \
+				(mesh, noComponents, cellValues, boundaryValues, boundaryPatch, boundaryDefinition)
+		
+		return boundaryValues
+
+
+	"""------------------------- General functions -----------------------"""
+	def setBoundaryValues(self, boundaryDefinition):
+		
+		mesh = self.data.mesh_
+		
+		boundaryValues = self.data.boundaryValues_
+		
+		cellValues = self.data.cellValues_
+		
+		noComponents = self.noComponents_
+		
+		# Loop over the boundaries, and assign corresponding values,
+		# depending on the boundary definition array
+		for boundaryPatch in mesh.connectivityData.boundary_:
+			boundaryName = boundaryPatch[0]
+			boundaryType = (boundaryDefinition[boundaryName])[0]
+			
+			setBoundaryValuesFunc = eval(									   \
+				"self.setBoundaryValuesFunctions." + boundaryType)
+			
+			setBoundaryValuesFunc											   \
+				(mesh, noComponents, cellValues, boundaryValues, boundaryPatch, boundaryDefinition)
+	
+	
+	def relax(self, fieldOld, fvSolutionParameters):
+		
+		try:
+			alpha = fvSolutionParameters['expUR']
+		except:
+			alpha = 1.0
+		
+		oldCellValues = fieldOld.data.cellValues_
+		newCellValues = self.data.cellValues_
+		
+		oldBoundaryValues = fieldOld.data.boundaryValues_
+		newBoundaryValues = self.data.boundaryValues_
+		
+		resultCellValues =													   \
+			oldCellValues + alpha * (newCellValues - oldCellValues)
+			
+		resultBoundaryValues = 												   \
+			oldBoundaryValues + alpha * (newBoundaryValues - oldBoundaryValues)
+		
+		self.data.cellValues_ = resultCellValues
+		self.data.boundaryValues_ = resultBoundaryValues
+	
+	
+	def setRefValue(self, fvSolution):
+		
+		boundaryDefinition = self.data.boundaryDefinition_
+		
+		# Loop over the boundaries and simply exit the function if there is a
+		# fixedValue boundary
+		for patch in boundaryDefinition:
+			
+			if ((boundaryDefinition[patch])[0] == 'fixedValue'):
+				return
+		
+		noComponents = self.noComponents_
+		
+		# Read refCell and readValue, otherwise set them to zero
+		try:
+			refCell = fvSolution['refCell']
+			refValue = fvSolution['refValue']
+		except:
+			refCell = 0
+			refValue = np.zeros(noComponents, dtype = float)
+		
+		cellValues = self.data.cellValues_
+		boundaryValues = self.data.boundaryValues_
+		
+		nCells = self.data.mesh_.geometryData.meshSize_
+		
+		for cmpt in range(noComponents):
+			deltaValue = refValue[cmpt] - cellValues[cmpt][refCell]
+			
+			cellValues[cmpt] += deltaValue
+			boundaryValues[cmpt] += deltaValue
+			
+	
+	# Copy an existing field
+	def copy(self):
+		
+		noComponents = self.noComponents_
+		
+		from src.OpenFOAM.fields.include import volScalarField, volVectorField
+		
+		resultClass = eval(self.className_)
+		
+		mesh = self.data.mesh_
+		
+		cellValues = np.copy(self.data.cellValues_)
+		boundaryValues = np.copy(self.data.boundaryValues_)
+		
+		boundaryDefinition = self.data.boundaryDefinition_
+
+		fieldName = self.data.fieldName_
+		
+		return resultClass(mesh, fieldName, boundaryValues, cellValues, boundaryDefinition)
+	
+	
+	# Multiply all cell and boundary values with cell volume
+	def integrateVolume(self):
+		
+		mesh = self.data.mesh_
+		
+		noComponents = self.noComponents_
+		
+		# Multiply cell values by volume
+		for cmpt in range(noComponents):
+			
+			self.data.cellValues_[cmpt] *= mesh.geometryData.V_
+		
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		nFacesTot = np.size(mesh.connectivityData.owner_)
+		
+		owner = mesh.connectivityData.owner_
+		
+		# Multiply boundary values by volume of the owner
+		for faceIndex in range(nInternalFaces, nFacesTot):
+			
+			boundaryCellIndex = owner[faceIndex]
+			
+			boundaryFaceIndex = faceIndex - nInternalFaces
+			
+			for cmpt in range(noComponents):
+				
+				self.data.boundaryValues_[cmpt][boundaryFaceIndex] *=		   \
+					mesh.geometryData.V_[boundaryCellIndex]
+	
+	
+	# Divide all cell and boundary values by cell volume
+	def divideVolume(self):
+		
+		mesh = self.data.mesh_
+		
+		noComponents = self.noComponents_
+		
+		# Divide cell values by volume
+		for cmpt in range(noComponents):
+			
+			self.data.cellValues_[cmpt] /= mesh.geometryData.V_
+		
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		nFacesTot = np.size(mesh.connectivityData.owner_)
+		
+		owner = mesh.connectivityData.owner_
+		
+		# Divide boundary values by volume of the owner
+		for faceIndex in range(nInternalFaces, nFacesTot):
+			
+			boundaryCellIndex = owner[faceIndex]
+			
+			boundaryFaceIndex = faceIndex - nInternalFaces
+			
+			for cmpt in range(noComponents):
+				
+				self.data.boundaryValues_[cmpt][boundaryFaceIndex] /=		   \
+					mesh.geometryData.V_[boundaryCellIndex]
+	
+	
+	# Write field values into the corresponding time folder
+	# If a file with the same name exists in the time folder, it is overwritten
+	def write(self, writeTime):
+		
+		# Local variable names
+		fieldTypeName = self.fieldTypeName_
+		className = self.className_
+		noComponents = self.noComponents_
+		
+		fieldName = self.data.fieldName_
+		
+		nCells = self.data.mesh_.geometryData.meshSize_
+		
+		# Create time directory if it does not exist
+		if os.path.exists(str(writeTime)):
+			None
+		else:
+			os.mkdir(str(writeTime))
+		
+		# Open the new file
+		file = open(str(writeTime) +'/' + fieldName, 'w')
+		
+		# Write the header and beginning of file
+		file.write(HEADER)
+		
+		file.write('    class       ' + className +';\n')
+		file.write('    location    ' + '"' + str(writeTime) + '"' + ';\n')
+		file.write('    object      ' + fieldName + ';\n}\n')
+		file.write('// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //\n\n')
+		file.write('dimensions      [0 0 0 0 0 0 0];\n\n')
+		file.write('internalField   nonuniform List<' + fieldTypeName + '>\n')
+		
+		file.write(str(nCells) + '\n(\n')
+		
+		# This function is in the individual class (volScalarField, etc.)
+		self.writeCellValues(file)
+			
+		file.write(');\n\n')
+		file.write('boundaryField\n{\n')
+		
+		self.writeBoundaryValues(file)
+			
+		file.write('}\n\n')
+		file.write('// ************************************************************************* //')
+	
+	# Write all components in the appropriate format
+	def writeCellValues(self, file):
+		
+		nCells = self.data.mesh_.geometryData.meshSize_
+		noComponents = self.noComponents_
+		cellValues = self.data.cellValues_
+		
+		for cellIndex in range(nCells):
+
+			file.write('(')
+			
+			# Write all components but the last with a trailing space symbol
+			for cmpt in range(noComponents - 1):
+				file.write(str(cellValues[cmpt][cellIndex]) + ' ')
+			
+			# Write the last component, close the bracket and insert new line
+			file.write(str(cellValues[noComponents - 1][cellIndex]) + ')\n')
+	
+	# Used to call the appropriate function in the individual class
+	# (volScalarField, etc.)
+	def writeBoundaryValues(self, file):
+		
+		boundaryDefinition = self.data.boundaryDefinition_
+		
+		boundaryValues = self.data.boundaryValues_
+		
+		mesh = self.data.mesh_
+		
+		# Write boundary values loop
+		for boundaryName in boundaryDefinition:
+			
+			boundaryType = (boundaryDefinition[boundaryName])[0]
+			
+			file.write('\t' + boundaryName + '\n')
+			file.write('\t' + '{\n')
+			file.write(2*'\t' + 'type' + 2*'\t' +							   \
+						(boundaryDefinition[boundaryName])[0] + ';\n')
+			
+			# Assign and call the appropriate function, depending on the
+			# boundaryType
+			writeBoundaryValuesFunc = eval(									   \
+				"self.writeBoundaryValuesFunctions." + 					   \
+				boundaryType)
+				
+			writeBoundaryValuesFunc(file, boundaryName, boundaryDefinition, mesh, boundaryValues)
+
+			file.write('\t' + '}\n\n')
+	
+	
+	"""------------------------ Defining operators --------------------------"""
+	
+	def __sub__(self, other): # - operator
+					
+		noComponents = self.noComponents_
+		
+		if (self.noComponents_ != other.noComponents_):
+			raise RuntimeError('Only volume fields with the same number of' +  \
+				' components can be subtracted!')
+				
+		from src.OpenFOAM.fields.include import volScalarField, volVectorField
+		
+		if (noComponents == 1):
+			resultClass = volScalarField
+		elif (noComponents == 3):
+			resultClass = volVectorField
+		
+		mesh = self.data.mesh_
+		
+		cellValues = self.data.cellValues_ - other.data.cellValues_
+		boundaryValues = self.data.boundaryValues_ - other.data.boundaryValues_
+		
+		selfBoundaryDefinition = self.data.boundaryDefinition_
+		resultBoundaryDefinition = dict.fromkeys(selfBoundaryDefinition)
+		
+		for selfPatch in selfBoundaryDefinition:
+			 
+			if (selfBoundaryDefinition[selfPatch][0] == 'empty'):
+				resultBoundaryDefinition[selfPatch] = selfBoundaryDefinition[selfPatch]
+			else:
+				resultBoundaryDefinition[selfPatch] = 							   \
+					('calculated', None)
+		
+		selfName = self.data.fieldName_
+		otherName = other.data.fieldName_
+		fieldName = selfName + "-" + otherName
+		
+		return resultClass(mesh, fieldName, boundaryValues, cellValues, resultBoundaryDefinition)
+		
+		
+	def __mul__(self, other): # * operator
+		
+		from src.OpenFOAM.fields.include import volScalarField, volVectorField
+		
+		noComponentsSelf = self.noComponents_
+		noComponentsOther = other.noComponents_
+		
+		# The number of components of the resulting field
+		noComponents = max(noComponentsSelf, noComponentsOther)
+		
+		# Choose the appropriate resulting class, depending on the class of the field
+		if (noComponents == 1):
+			resultClass = volScalarField
+		elif (noComponents == 3):
+			resultClass = volVectorField
+		else:
+			raise RuntimeError("Error in multiplication of vector fields!")
+		
+		selfName = self.data.fieldName_
+		otherName = other.data.fieldName_
+		resultName = selfName + "*" + otherName
+		
+		mesh = self.data.mesh_
+
+		resultInternalField = np.zeros(noComponents, dtype=float)
+		
+		selfBoundaryDefinition = self.data.boundaryDefinition_
+		resultBoundaryDefinition = dict.fromkeys(selfBoundaryDefinition)
+		
+		for selfPatch in selfBoundaryDefinition:
+			 
+			if (selfBoundaryDefinition[selfPatch][0] == 'empty'):
+				resultBoundaryDefinition[selfPatch] = selfBoundaryDefinition[selfPatch]
+			else:
+				resultBoundaryDefinition[selfPatch] = 							   \
+					('calculated', None)
+		
+		resultVolField = resultClass.initialize(							   \
+			resultName, mesh, resultBoundaryDefinition, resultInternalField)
+		
+		nCells = self.data.mesh_.geometryData.meshSize_
+		
+		nBoundaryFaces = 													   \
+			np.size(mesh.connectivityData.owner_) - np.size(mesh.connectivityData.neighbour_)
+		
+		resultCellValues = np.empty((noComponents, nCells), dtype = float)
+		resultBoundaryValues = np.empty((noComponents, nBoundaryFaces), dtype = float)
+		
+		selfCellValues = self.data.cellValues_
+		selfBoundaryValues = self.data.boundaryValues_
+		otherCellValues = other.data.cellValues_
+		otherBoundaryValues = other.data.boundaryValues_
+		
+		resultCmpt = 0
+		for selfCmpt in range(noComponentsSelf):
+			for otherCmpt in range(noComponentsOther):
+				
+				resultCellValues[resultCmpt] = selfCellValues[selfCmpt] * otherCellValues[otherCmpt]
+				resultBoundaryValues[resultCmpt] = selfBoundaryValues[selfCmpt] * otherBoundaryValues[otherCmpt]
+				
+				resultCmpt += 1
+				
+		resultVolField.data.cellValues_ = resultCellValues
+		resultVolField.data.boundaryValues_ = resultBoundaryValues
+		
+		return resultVolField
+		
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/volField/volFieldBoundaryConditions.py b/foamPython/src/OpenFOAM/fields/volField/volFieldBoundaryConditions.py
new file mode 100644
index 0000000000000000000000000000000000000000..597331965fcd3aef7eb53b3cdf0e9b25a7caecc1
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/volField/volFieldBoundaryConditions.py
@@ -0,0 +1,195 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    volField class boundary conditions functions
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+class volFieldBoundaryConditions:
+	
+	# Functions used to set the appropriate values in the boundaryValues array.
+	# This is later used for writing results, calculating gradient, etc.
+	class setBoundaryValuesFunctions:
+		
+		def fixedValue														   \
+		   (mesh, noComponents, cellValues, boundaryValues, boundaryPatch, boundaryDefinition):
+
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			# Boundary properties
+			boundaryName = boundaryPatch[0]
+			nFaces = boundaryPatch[2]
+			startFace = boundaryPatch[3]
+			
+			# Start and end indices inside the boundaryValues array
+			boundaryValuesStart = startFace - nInternalFaces
+			boundaryValuesEnd = boundaryValuesStart + nFaces
+			
+			owner = mesh.connectivityData.owner_
+			
+			specifiedFixedValue = (boundaryDefinition[boundaryName])[1]
+			
+			# Assign the appropriate values to the appropriate part of the 
+			# boundaryValues array
+			for cmpt in range(noComponents):
+				
+				for faceIndex in range(startFace, startFace + nFaces):
+					
+					boundaryFaceIndex = faceIndex - nInternalFaces
+					
+					boundaryCellIndex = owner[faceIndex]
+					
+					boundaryValues[cmpt][boundaryFaceIndex]					   \
+						= specifiedFixedValue[cmpt]
+					
+					
+		def empty															   \
+		   (mesh, noComponents, cellValues, boundaryValues, boundaryPatch, boundaryDefinition):
+			   
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			owner = mesh.connectivityData.owner_
+			
+			# Boundary properties
+			boundaryName = boundaryPatch[0]
+			nFaces = boundaryPatch[2]
+			startFace = boundaryPatch[3]
+			
+			for cmpt in range(noComponents):
+				for faceIndex in range(startFace, startFace + nFaces):
+					
+					boundaryFaceIndex = faceIndex - nInternalFaces
+					
+					boundaryCellIndex = owner[faceIndex]
+					
+					boundaryValues[cmpt][boundaryFaceIndex]					   \
+						= cellValues[cmpt][boundaryCellIndex]
+						
+		def fixedGradient													   \
+		   (mesh, noComponents, cellValues, boundaryValues, boundaryPatch, boundaryDefinition):
+			   
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			owner = mesh.connectivityData.owner_
+			
+			# Boundary properties
+			boundaryName = boundaryPatch[0]
+			nFaces = boundaryPatch[2]
+			startFace = boundaryPatch[3]
+			
+			# Cell centres
+			C = mesh.geometryData.C_
+			# Face centres
+			Cf = mesh.geometryData.Cf_
+			
+			specifiedFixedGradient = (boundaryDefinition[boundaryName])[1]
+			
+			# Assign the appropriate values to the appropriate part of the 
+			# boundaryValues array
+			for cmpt in range(noComponents):
+				for faceIndex in range(startFace, startFace + nFaces):
+					
+					boundaryFaceIndex = faceIndex - nInternalFaces
+					
+					boundaryCellIndex = owner[faceIndex]
+					
+					boundaryValues[cmpt][boundaryFaceIndex]					   \
+						= cellValues[cmpt][boundaryCellIndex] + 			   \
+						specifiedFixedGradient[cmpt] *						   \
+						np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex])
+						
+		def calculated														   \
+			(mesh, noComponents, cellValues, boundaryValues, boundaryPatch, boundaryDefinition):
+			
+			None
+	
+	# Functions used to set the appropriate values in the boundaryValues array
+	# when calculating the gradient of a field.
+	class setBoundaryValuesFunctionsGrad:
+		
+		def fixedValue														   \
+		   (mesh, noComponentsGrad, cellValues, field, boundaryValues, boundaryPatch, boundaryDefinition):
+			   
+			C = mesh.geometryData.C_
+			Cf = mesh.geometryData.Cf_
+			
+			noComponentsField = field.noComponents_
+			
+			fieldCellValues = field.data.cellValues_
+			fieldBoundaryValues = field.data.boundaryValues_
+			
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			# Boundary properties
+			boundaryName = boundaryPatch[0]
+			nFaces = boundaryPatch[2]
+			startFace = boundaryPatch[3]
+			
+			owner = mesh.connectivityData.owner_
+			
+			# For all boundary faces, directly evaluate the gradient using given 
+			# field values
+			for faceIndex in range(startFace, startFace + nFaces):
+				
+				boundaryCellIndex = owner[faceIndex]
+				
+				boundaryFaceIndex = faceIndex - nInternalFaces
+				
+				# For all grad components
+				cmptGrad = 0
+				# For all field components
+				for cmptField in range(noComponentsField):
+					# For all distance vector components (3)
+					for cmptD in range(3):
+						
+						cellCentre = C[boundaryCellIndex]
+						faceCentre = Cf[boundaryCellIndex]
+						
+						gradFaceCell =										   \
+							(fieldCellValues[cmptField][boundaryCellIndex] - fieldBoundaryValues[cmptField][boundaryFaceIndex])
+							
+						distFaceCell = 										   \
+							(C[boundaryCellIndex][cmptD] - Cf[boundaryFaceIndex][cmptD])
+						
+						# If the face centre and the cell centre are at the same
+						# location in a given coordinate axis, copy the value of
+						# the cell gradient to the face
+						if (abs(distFaceCell) < 1e-10):
+							boundaryValues[cmptGrad][boundaryFaceIndex] =	   \
+								cellValues[cmptGrad][boundaryCellIndex]
+						else:
+							boundaryValues[cmptGrad][boundaryFaceIndex] =	   \
+							 (fieldCellValues[cmptField][boundaryCellIndex] - fieldBoundaryValues[cmptField][boundaryFaceIndex]) / \
+							 (C[boundaryCellIndex][cmptD] - Cf[boundaryFaceIndex][cmptD])
+						
+						cmptGrad += 1
+		
+		
+		# All face gradients are calculated using the pre-existing field values
+		# in cells and boundary faces. For that reason, boundary calculation
+		# functions are the same for all boundary conditions:
+		
+		empty = fixedValue
+		
+		fixedGradient = fixedValue
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/volField/volScalarField/volScalarField.py b/foamPython/src/OpenFOAM/fields/volField/volScalarField/volScalarField.py
new file mode 100644
index 0000000000000000000000000000000000000000..3f361b10bf65f12325de3b6f4a1ee6613d01400e
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/volField/volScalarField/volScalarField.py
@@ -0,0 +1,170 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Volume scalar field class.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.OpenFOAM.fields.volField.volField import *
+
+
+class volScalarField(volField):
+	
+	fieldTypeName_ = "scalar"
+	className_ = "volScalarField"
+	noComponents_ = 1
+	componentsNames_ = ("",)
+	
+	"""------------------------- Constructors -------------------------------"""
+	
+	# Constructor used to calculate the divergence of a volField
+	@classmethod
+	def div(self, field):
+		
+		mesh = field.data.mesh_
+		
+		fieldName = "div" + field.data.fieldName_
+		
+		boundaryDefinitionField = field.data.boundaryDefinition_
+		boundaryDefinition = dict.fromkeys(boundaryDefinitionField)
+		
+		for fieldPatch in boundaryDefinitionField:
+			 
+			if (boundaryDefinitionField[fieldPatch][0] == 'empty'):
+				boundaryDefinition[fieldPatch] = boundaryDefinitionField[fieldPatch]
+			else:
+				boundaryDefinition[fieldPatch] = 							   \
+					('calculated', None)
+		
+		fieldClass = field.className_
+		
+		cellValuesFunc = eval("self.setCellValuesDiv" + fieldClass)
+		boundaryValuesFunc = eval("self.setBoundaryValuesInitialDiv" + fieldClass)
+		
+		# Set cell values
+		cellValues		= cellValuesFunc(field)
+		
+		# Set boundary values
+		boundaryValues  = boundaryValuesFunc(cellValues, field)
+		
+		return self(mesh, fieldName, boundaryValues, cellValues, boundaryDefinition)
+	
+	
+	"""------------------------ Initialization ------------------------------"""
+	@classmethod
+	def setCellValuesDivsurfaceScalarField(self, field):
+		
+		noComponents = 1
+		
+		mesh = field.data.mesh_
+		
+		nCells = mesh.geometryData.meshSize_
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		nFacesTot = np.size(mesh.connectivityData.owner_)
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		faceValues = field.data.faceValues_
+		
+		cellValues = np.zeros((noComponents, nCells), dtype = float)
+		
+		# Loop over internal faces
+		for faceIndex in range(nInternalFaces):
+			
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			cellValues[0][ownIndex] += faceValues[0][faceIndex]
+			cellValues[0][neiIndex] -= faceValues[0][faceIndex]
+		
+		# Loop over boundary faces
+		for faceIndex in range(nInternalFaces, nFacesTot):
+			
+			boundaryCellIndex = owner[faceIndex]
+			
+			cellValues[0][boundaryCellIndex] += faceValues[0][faceIndex]
+		
+		cellValues[0] /= mesh.geometryData.V_
+		
+		return cellValues
+		
+	@classmethod
+	def setBoundaryValuesInitialDivsurfaceScalarField(self, cellValues, field):
+		
+		noComponents = 1
+		
+		mesh = field.data.mesh_
+		
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		nFacesTot = np.size(mesh.connectivityData.owner_)
+		nFacesBound = nFacesTot - nInternalFaces
+		
+		owner = mesh.connectivityData.owner_
+		
+		boundaryValues = np.empty((noComponents, nFacesBound), dtype = float)
+		
+		for faceIndex in range(nInternalFaces, nFacesTot):
+			
+			boundaryFaceIndex = faceIndex - nInternalFaces
+			
+			boundaryCellIndex = owner[faceIndex]
+			
+			# Zero gradient extrapolation
+			boundaryValues[0][boundaryFaceIndex] = cellValues[0][boundaryCellIndex]
+
+		return boundaryValues
+	
+	
+	"""----------------------- General functions ----------------------------"""
+	
+	
+	# Write only one component
+	def writeCellValues(self, file):
+		
+		nCells = self.data.mesh_.geometryData.meshSize_
+		noComponents = self.noComponents_
+		cellValues = self.data.cellValues_
+		
+		for cellIndex in range(nCells):
+
+			# Write all components but the last with a trailing space symbol
+			for cmpt in range(noComponents - 1):
+				file.write(str(cellValues[cmpt][cellIndex]) + ' ')
+			
+			# Write the last component, close the bracket and insert new line
+			file.write(str(cellValues[noComponents - 1][cellIndex]) + '\n')
+			
+	class writeBoundaryValuesFunctions:
+		
+		def fixedValue(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			fValue = (boundaryDefinition[boundaryName])[1]
+			
+			file.write(2*'\t' + 'value' + 2*'\t' + 'uniform ' +				   \
+						   str(fValue[0]) + ';\n')
+		
+		def empty(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			None
+			
+		def fixedGradient(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			fGradient = (boundaryDefinition[boundaryName])[1]
+			
+			file.write(2*'\t' + 'gradient' + 2*'\t' + 'uniform ' +			   \
+						   str(fGradient[0]) + ';\n')
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/volField/volVectorField/setup_volVectorField.py b/foamPython/src/OpenFOAM/fields/volField/volVectorField/setup_volVectorField.py
new file mode 100644
index 0000000000000000000000000000000000000000..6fd57f8ae3b20c6686b13c212e17c2c451666c66
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/volField/volVectorField/setup_volVectorField.py
@@ -0,0 +1,31 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Setup file for Cythonizing volVectorField
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import os
+
+from distutils.core import setup
+from Cython.Build import cythonize
+
+directives = {'language_level': 3}
+
+currDir = os.path.dirname(os.path.abspath(__file__))
+setup(ext_modules = cythonize(currDir + '/volVectorField.pyx', compiler_directives=directives))
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/volField/volVectorField/volVectorField.py b/foamPython/src/OpenFOAM/fields/volField/volVectorField/volVectorField.py
new file mode 100644
index 0000000000000000000000000000000000000000..39627dc37d27c530deda25875398ce3f8f1d2691
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/volField/volVectorField/volVectorField.py
@@ -0,0 +1,259 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Volume vector field class, pure Python.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.OpenFOAM.fields.volField.volField import *
+
+
+
+class volVectorField(volField):
+	
+	fieldTypeName_ = "vector"
+	className_ = "volVectorField"
+	noComponents_ = 3
+	componentsNames_ = ("x", "y", "z")
+	
+	# Constructor used to calculate the gradient of a volField
+	@classmethod
+	def grad(self, field):
+		
+		mesh = field.data.mesh_
+		
+		fieldName = "grad" + field.data.fieldName_
+		
+		boundaryDefinitionField = field.data.boundaryDefinition_
+		boundaryDefinition = dict.fromkeys(boundaryDefinitionField)
+		
+		for fieldPatch in boundaryDefinitionField:
+			 
+			if (boundaryDefinitionField[fieldPatch][0] == 'empty'):
+				boundaryDefinition[fieldPatch] = boundaryDefinitionField[fieldPatch]
+			else:
+				boundaryDefinition[fieldPatch] = 							   \
+					('calculated', None)
+		
+		# Set cell values
+		cellValues		= self.setCellValuesGrad(field)
+		
+		# Set boundary values
+		boundaryValues  = self.setBoundaryValuesInitialGrad(cellValues, field)
+		
+		return self(mesh, fieldName, boundaryValues, cellValues, boundaryDefinition)
+	
+	
+	@classmethod
+	def setCellValuesGrad(self, field):
+		
+		mesh = field.data.mesh_
+		
+		C = mesh.geometryData.C_
+		Cf = mesh.geometryData.Cf_
+		Sf = mesh.geometryData.Sf_
+		V = mesh.geometryData.V_
+		
+		# Number of components of the field to make a gradient of
+		noComponentsField = field.noComponents_
+		
+		# The tensor rank of the result is 1 higher than the field rank
+		noComponentsGrad = field.noComponents_ * 3
+		
+		# Total number of cells
+		nCells = mesh.read("geometryData.meshSize")
+		# Number of internal faces
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		# Total number of faces
+		nFacesTot = np.size(mesh.connectivityData.owner_)
+		
+		cellValues = field.data.cellValues_
+		
+		gradField = np.zeros((noComponentsGrad, nCells), dtype = float)
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		interpCellValues = np.empty(noComponentsField, dtype = float)
+		
+		# For all internal faces
+		for faceIndex in range(nInternalFaces):
+				
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			absPf = np.linalg.norm(Cf[faceIndex] - C[ownIndex])
+			absNf = np.linalg.norm(Cf[faceIndex] - C[neiIndex])
+			
+			f = absNf / (absPf + absNf)
+			
+			# For all field components, interpolate value to face
+			for cmpt in range(noComponentsField):
+							
+				interpCellValues[cmpt] = 									   \
+					f * cellValues[cmpt][ownIndex] + 						   \
+					(1 - f) * cellValues[cmpt][neiIndex]
+			
+			# For all grad components
+			cmptGrad = 0
+			# For all Sf components (3)
+			for cmptSf in range(3):
+				# For all field components
+				for cmptField in range(noComponentsField):
+					# int_V gradField dV = sum_f Sf * field_f > owner
+					gradField[cmptGrad][ownIndex] +=						   \
+						interpCellValues[cmptField] * 						   \
+						Sf[faceIndex][cmptSf]
+					
+					# int_V gradField dV = - sum_f Sf * field_f > neighbour
+					gradField[cmptGrad][neiIndex] -=						   \
+						interpCellValues[cmptField] * 						   \
+						Sf[faceIndex][cmptSf]
+					
+					# Increment gradient result component counter
+					cmptGrad += 1
+		
+		boundaryValues = field.data.boundaryValues_
+		
+		# For all boundary faces
+		for faceIndex in range(nInternalFaces, nFacesTot):
+			
+			boundaryCellIndex = owner[faceIndex]
+			
+			boundaryFaceIndex = faceIndex - nInternalFaces
+			
+			# For all grad components
+			cmptGrad = 0
+			# For all Sf components (3)
+			for cmptSf in range(3):
+				# For all field components
+				for cmptField in range(noComponentsField):
+					# int_V gradField dV = sum_f Sf * field_f
+					gradField[cmptGrad][boundaryCellIndex] +=				   \
+						boundaryValues[cmptField][boundaryFaceIndex] *		   \
+						Sf[faceIndex][cmptSf]
+						
+					# Increment gradient result component counter
+					cmptGrad += 1
+			
+		
+		# Divide all cell values by the volume of the specific cell. The
+		# previously calculated gradient values were actually volume integrals
+		for cmptGrad in range(noComponentsGrad):
+			gradField[cmptGrad] /= V
+		
+		
+		return gradField
+	
+	
+	@classmethod
+	def setBoundaryValuesInitialGrad(self, cellValues, field):
+		
+		boundaryDefinition = field.data.boundaryDefinition_
+		
+		mesh = field.data.mesh_
+		
+		# Define the required parameters and set the boundary values
+		nFacesBound = np.size(mesh.connectivityData.owner_)					   \
+					  - np.size(mesh.connectivityData.neighbour_)
+		
+		noComponents = self.noComponents_
+		
+		# Initialize the boundary array
+		boundaryValues = np.empty((noComponents, nFacesBound), dtype = float)
+		
+		# Loop over the boundaries, and assign corresponding values,
+		# depending on the boundary definition array
+		for boundaryPatch in mesh.connectivityData.boundary_:
+			boundaryName = boundaryPatch[0]
+			boundaryType = (boundaryDefinition[boundaryName])[0]
+			
+			setBoundaryValuesFunc = eval(									   \
+				"self.setBoundaryValuesFunctionsGrad." + boundaryType)
+			
+			setBoundaryValuesFunc											   \
+				(mesh, noComponents, cellValues, field, boundaryValues, boundaryPatch, boundaryDefinition)
+		
+		return boundaryValues
+	
+	
+	class writeBoundaryValuesFunctions:
+		
+		def fixedValue(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			fValue = (boundaryDefinition[boundaryName])[1]
+			
+			file.write(2*'\t' + 'value' + 2*'\t' + 'uniform ')
+			file.write('(')
+			# X component
+			file.write(str(fValue[0]) + ' ')
+			# Y component
+			file.write(str(fValue[1]) + ' ')
+			# Z component
+			file.write(str(fValue[2]) + ');\n')
+		
+		def empty(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			None
+			
+		def fixedGradient(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			fGradient = (boundaryDefinition[boundaryName])[1]
+			
+			file.write(2*'\t' + 'gradient' + 2*'\t' + 'uniform ')
+			file.write('(')
+			# X component
+			file.write(str(fGradient[0]) + ' ')
+			# Y component
+			file.write(str(fGradient[1]) + ' ')
+			# Z component
+			file.write(str(fGradient[2]) + ');\n')
+			
+		def calculated(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			for i in mesh.connectivityData.boundary_:
+				
+				if (i[0] == boundaryName):
+					boundaryPatch = i
+					break
+					
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			nFaces = boundaryPatch[2]
+			startFace = boundaryPatch[3]
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+			
+			file.write(2*'\t' + 'value' + 2*'\t' + 'nonuniform List<vector>\n')
+			file.write(str(nFaces) + '\n')
+			file.write('(\n')
+			
+			for faceIndex in range(startFace, startFace + nFaces):
+				
+				boundaryFaceIndex = faceIndex - nInternalFaces
+				
+				file.write('(')
+				# X component
+				file.write(str(boundaryValues[0][boundaryFaceIndex]) + ' ')
+				# Y component
+				file.write(str(boundaryValues[1][boundaryFaceIndex]) + ' ')
+				# Z component
+				file.write(str(boundaryValues[2][boundaryFaceIndex]) + ')\n')
+			
+			file.write(');\n')
+			
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/volField/volVectorField/volVectorField.py.orig b/foamPython/src/OpenFOAM/fields/volField/volVectorField/volVectorField.py.orig
new file mode 100644
index 0000000000000000000000000000000000000000..39627dc37d27c530deda25875398ce3f8f1d2691
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/volField/volVectorField/volVectorField.py.orig
@@ -0,0 +1,259 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Volume vector field class, pure Python.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.OpenFOAM.fields.volField.volField import *
+
+
+
+class volVectorField(volField):
+	
+	fieldTypeName_ = "vector"
+	className_ = "volVectorField"
+	noComponents_ = 3
+	componentsNames_ = ("x", "y", "z")
+	
+	# Constructor used to calculate the gradient of a volField
+	@classmethod
+	def grad(self, field):
+		
+		mesh = field.data.mesh_
+		
+		fieldName = "grad" + field.data.fieldName_
+		
+		boundaryDefinitionField = field.data.boundaryDefinition_
+		boundaryDefinition = dict.fromkeys(boundaryDefinitionField)
+		
+		for fieldPatch in boundaryDefinitionField:
+			 
+			if (boundaryDefinitionField[fieldPatch][0] == 'empty'):
+				boundaryDefinition[fieldPatch] = boundaryDefinitionField[fieldPatch]
+			else:
+				boundaryDefinition[fieldPatch] = 							   \
+					('calculated', None)
+		
+		# Set cell values
+		cellValues		= self.setCellValuesGrad(field)
+		
+		# Set boundary values
+		boundaryValues  = self.setBoundaryValuesInitialGrad(cellValues, field)
+		
+		return self(mesh, fieldName, boundaryValues, cellValues, boundaryDefinition)
+	
+	
+	@classmethod
+	def setCellValuesGrad(self, field):
+		
+		mesh = field.data.mesh_
+		
+		C = mesh.geometryData.C_
+		Cf = mesh.geometryData.Cf_
+		Sf = mesh.geometryData.Sf_
+		V = mesh.geometryData.V_
+		
+		# Number of components of the field to make a gradient of
+		noComponentsField = field.noComponents_
+		
+		# The tensor rank of the result is 1 higher than the field rank
+		noComponentsGrad = field.noComponents_ * 3
+		
+		# Total number of cells
+		nCells = mesh.read("geometryData.meshSize")
+		# Number of internal faces
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		# Total number of faces
+		nFacesTot = np.size(mesh.connectivityData.owner_)
+		
+		cellValues = field.data.cellValues_
+		
+		gradField = np.zeros((noComponentsGrad, nCells), dtype = float)
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		interpCellValues = np.empty(noComponentsField, dtype = float)
+		
+		# For all internal faces
+		for faceIndex in range(nInternalFaces):
+				
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			absPf = np.linalg.norm(Cf[faceIndex] - C[ownIndex])
+			absNf = np.linalg.norm(Cf[faceIndex] - C[neiIndex])
+			
+			f = absNf / (absPf + absNf)
+			
+			# For all field components, interpolate value to face
+			for cmpt in range(noComponentsField):
+							
+				interpCellValues[cmpt] = 									   \
+					f * cellValues[cmpt][ownIndex] + 						   \
+					(1 - f) * cellValues[cmpt][neiIndex]
+			
+			# For all grad components
+			cmptGrad = 0
+			# For all Sf components (3)
+			for cmptSf in range(3):
+				# For all field components
+				for cmptField in range(noComponentsField):
+					# int_V gradField dV = sum_f Sf * field_f > owner
+					gradField[cmptGrad][ownIndex] +=						   \
+						interpCellValues[cmptField] * 						   \
+						Sf[faceIndex][cmptSf]
+					
+					# int_V gradField dV = - sum_f Sf * field_f > neighbour
+					gradField[cmptGrad][neiIndex] -=						   \
+						interpCellValues[cmptField] * 						   \
+						Sf[faceIndex][cmptSf]
+					
+					# Increment gradient result component counter
+					cmptGrad += 1
+		
+		boundaryValues = field.data.boundaryValues_
+		
+		# For all boundary faces
+		for faceIndex in range(nInternalFaces, nFacesTot):
+			
+			boundaryCellIndex = owner[faceIndex]
+			
+			boundaryFaceIndex = faceIndex - nInternalFaces
+			
+			# For all grad components
+			cmptGrad = 0
+			# For all Sf components (3)
+			for cmptSf in range(3):
+				# For all field components
+				for cmptField in range(noComponentsField):
+					# int_V gradField dV = sum_f Sf * field_f
+					gradField[cmptGrad][boundaryCellIndex] +=				   \
+						boundaryValues[cmptField][boundaryFaceIndex] *		   \
+						Sf[faceIndex][cmptSf]
+						
+					# Increment gradient result component counter
+					cmptGrad += 1
+			
+		
+		# Divide all cell values by the volume of the specific cell. The
+		# previously calculated gradient values were actually volume integrals
+		for cmptGrad in range(noComponentsGrad):
+			gradField[cmptGrad] /= V
+		
+		
+		return gradField
+	
+	
+	@classmethod
+	def setBoundaryValuesInitialGrad(self, cellValues, field):
+		
+		boundaryDefinition = field.data.boundaryDefinition_
+		
+		mesh = field.data.mesh_
+		
+		# Define the required parameters and set the boundary values
+		nFacesBound = np.size(mesh.connectivityData.owner_)					   \
+					  - np.size(mesh.connectivityData.neighbour_)
+		
+		noComponents = self.noComponents_
+		
+		# Initialize the boundary array
+		boundaryValues = np.empty((noComponents, nFacesBound), dtype = float)
+		
+		# Loop over the boundaries, and assign corresponding values,
+		# depending on the boundary definition array
+		for boundaryPatch in mesh.connectivityData.boundary_:
+			boundaryName = boundaryPatch[0]
+			boundaryType = (boundaryDefinition[boundaryName])[0]
+			
+			setBoundaryValuesFunc = eval(									   \
+				"self.setBoundaryValuesFunctionsGrad." + boundaryType)
+			
+			setBoundaryValuesFunc											   \
+				(mesh, noComponents, cellValues, field, boundaryValues, boundaryPatch, boundaryDefinition)
+		
+		return boundaryValues
+	
+	
+	class writeBoundaryValuesFunctions:
+		
+		def fixedValue(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			fValue = (boundaryDefinition[boundaryName])[1]
+			
+			file.write(2*'\t' + 'value' + 2*'\t' + 'uniform ')
+			file.write('(')
+			# X component
+			file.write(str(fValue[0]) + ' ')
+			# Y component
+			file.write(str(fValue[1]) + ' ')
+			# Z component
+			file.write(str(fValue[2]) + ');\n')
+		
+		def empty(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			None
+			
+		def fixedGradient(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			fGradient = (boundaryDefinition[boundaryName])[1]
+			
+			file.write(2*'\t' + 'gradient' + 2*'\t' + 'uniform ')
+			file.write('(')
+			# X component
+			file.write(str(fGradient[0]) + ' ')
+			# Y component
+			file.write(str(fGradient[1]) + ' ')
+			# Z component
+			file.write(str(fGradient[2]) + ');\n')
+			
+		def calculated(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			for i in mesh.connectivityData.boundary_:
+				
+				if (i[0] == boundaryName):
+					boundaryPatch = i
+					break
+					
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			nFaces = boundaryPatch[2]
+			startFace = boundaryPatch[3]
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+			
+			file.write(2*'\t' + 'value' + 2*'\t' + 'nonuniform List<vector>\n')
+			file.write(str(nFaces) + '\n')
+			file.write('(\n')
+			
+			for faceIndex in range(startFace, startFace + nFaces):
+				
+				boundaryFaceIndex = faceIndex - nInternalFaces
+				
+				file.write('(')
+				# X component
+				file.write(str(boundaryValues[0][boundaryFaceIndex]) + ' ')
+				# Y component
+				file.write(str(boundaryValues[1][boundaryFaceIndex]) + ' ')
+				# Z component
+				file.write(str(boundaryValues[2][boundaryFaceIndex]) + ')\n')
+			
+			file.write(');\n')
+			
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/volField/volVectorField/volVectorField_cy.pyx b/foamPython/src/OpenFOAM/fields/volField/volVectorField/volVectorField_cy.pyx
new file mode 100644
index 0000000000000000000000000000000000000000..e3b392ccca49a3844c4a91c225e9e5588aed2b23
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/volField/volVectorField/volVectorField_cy.pyx
@@ -0,0 +1,304 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Volume vector field class, with Cython.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.OpenFOAM.fields.volField.volField import *
+import numpy as np
+cimport numpy as np
+
+class volVectorField(volField):
+	
+	fieldTypeName_ = "vector"
+	className_ = "volVectorField"
+	noComponents_ = 3
+	componentsNames_ = ("x", "y", "z")
+	
+	# Constructor used to calculate the gradient of a volField
+	@classmethod
+	def grad(self, field):
+		
+		mesh = field.data.mesh_
+		
+		fieldName = "grad" + field.data.fieldName_
+		
+		boundaryDefinitionField = field.data.boundaryDefinition_
+		boundaryDefinition = dict.fromkeys(boundaryDefinitionField)
+		
+		for fieldPatch in boundaryDefinitionField:
+			 
+			if (boundaryDefinitionField[fieldPatch][0] == 'empty'):
+				boundaryDefinition[fieldPatch] = boundaryDefinitionField[fieldPatch]
+			else:
+				boundaryDefinition[fieldPatch] = 							   \
+					('calculated', None)
+		
+		# Set cell values
+		cellValues		= self.setCellValuesGrad(field)
+		
+		# Set boundary values
+		boundaryValues  = self.setBoundaryValuesInitialGrad(cellValues, field)
+		
+		return self(mesh, fieldName, boundaryValues, cellValues, boundaryDefinition)
+	
+	
+	@classmethod
+	def setCellValuesGrad(self, field):
+		
+		"""------------------- Cython declarations --------------------------"""
+#		cdef int nInternalFaces
+#		cdef int faceIndex
+#		cdef int ownIndex
+#		cdef int neiIndex
+		
+#		cdef np.ndarray[np.float_t, ndim=1] psi
+#		cdef np.ndarray[np.float_t, ndim=1] lower
+#		cdef np.ndarray[np.float_t, ndim=1] diag
+#		cdef np.ndarray[np.float_t, ndim=1] upper
+#		cdef np.ndarray[np.int_t, ndim=1] owner
+#		cdef np.ndarray[np.int_t, ndim=1] neighbour
+#		cdef np.ndarray[np.float_t, ndim=1] resultArray
+		
+		cdef int noComponentsField
+		cdef int noComponentsGrad
+		cdef int nCells
+		cdef int nInternalFaces
+		cdef int nFacesTot
+		cdef int faceIndex
+		cdef int ownIndex
+		cdef int neiIndex
+		cdef float absPf
+		cdef float absNf
+		cdef float f
+		cdef int cmpt
+		cdef int cmptGrad
+		cdef int cmptSf
+		cdef int cmptField
+		cdef int boundaryCellIndex
+		cdef int boundaryFaceIndex
+		
+		cdef np.ndarray[np.float_t, ndim=2] C
+		cdef np.ndarray[np.float_t, ndim=2] Cf
+		cdef np.ndarray[np.float_t, ndim=2] Sf
+		cdef np.ndarray[np.float_t, ndim=1] V
+		cdef np.ndarray[np.float_t, ndim=2] cellValues
+		cdef np.ndarray[np.float_t, ndim=2] gradField
+		cdef np.ndarray[np.int_t, ndim=1] owner
+		cdef np.ndarray[np.int_t, ndim=1] neighbour
+		cdef np.ndarray[np.float_t, ndim=1] interpCellValues
+		cdef np.ndarray[np.float_t, ndim=2] boundaryValues
+		
+		"""------------------------------------------------------------------"""
+		
+		mesh = field.data.mesh_
+		
+		C = mesh.geometryData.C_
+		Cf = mesh.geometryData.Cf_
+		Sf = mesh.geometryData.Sf_
+		V = mesh.geometryData.V_
+		
+		# Number of components of the field to make a gradient of
+		noComponentsField = field.noComponents_
+		
+		# The tensor rank of the result is 1 higher than the field rank
+		noComponentsGrad = field.noComponents_ * 3
+		
+		# Total number of cells
+		nCells = mesh.read("geometryData.meshSize")
+		# Number of internal faces
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		# Total number of faces
+		nFacesTot = np.size(mesh.connectivityData.owner_)
+		
+		cellValues = field.data.cellValues_
+		
+		gradField = np.zeros((noComponentsGrad, nCells), dtype = float)
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		interpCellValues = np.empty(noComponentsField, dtype = float)
+		
+		# For all internal faces
+		for faceIndex in range(nInternalFaces):
+				
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			absPf = np.linalg.norm(Cf[faceIndex] - C[ownIndex])
+			absNf = np.linalg.norm(Cf[faceIndex] - C[neiIndex])
+			
+			f = absNf / (absPf + absNf)
+			
+			# For all field components, interpolate value to face
+			for cmpt in range(noComponentsField):
+							
+				interpCellValues[cmpt] = 									   \
+					f * cellValues[cmpt][ownIndex] + 						   \
+					(1 - f) * cellValues[cmpt][neiIndex]
+			
+			# For all grad components
+			cmptGrad = 0
+			# For all Sf components (3)
+			for cmptSf in range(3):
+				# For all field components
+				for cmptField in range(noComponentsField):
+					# int_V gradField dV = sum_f Sf * field_f > owner
+					gradField[cmptGrad][ownIndex] +=						   \
+						interpCellValues[cmptField] * 						   \
+						Sf[faceIndex][cmptSf]
+					
+					# int_V gradField dV = - sum_f Sf * field_f > neighbour
+					gradField[cmptGrad][neiIndex] -=						   \
+						interpCellValues[cmptField] * 						   \
+						Sf[faceIndex][cmptSf]
+					
+					# Increment gradient result component counter
+					cmptGrad += 1
+		
+		boundaryValues = field.data.boundaryValues_
+		
+		# For all boundary faces
+		for faceIndex in range(nInternalFaces, nFacesTot):
+			
+			boundaryCellIndex = owner[faceIndex]
+			
+			boundaryFaceIndex = faceIndex - nInternalFaces
+			
+			# For all grad components
+			cmptGrad = 0
+			# For all Sf components (3)
+			for cmptSf in range(3):
+				# For all field components
+				for cmptField in range(noComponentsField):
+					# int_V gradField dV = sum_f Sf * field_f
+					gradField[cmptGrad][boundaryCellIndex] +=				   \
+						boundaryValues[cmptField][boundaryFaceIndex] *		   \
+						Sf[faceIndex][cmptSf]
+						
+					# Increment gradient result component counter
+					cmptGrad += 1
+			
+		
+		# Divide all cell values by the volume of the specific cell. The
+		# previously calculated gradient values were actually volume integrals
+		for cmptGrad in range(noComponentsGrad):
+			gradField[cmptGrad] /= V
+		
+		
+		return gradField
+	
+	
+	@classmethod
+	def setBoundaryValuesInitialGrad(self, cellValues, field):
+		
+		boundaryDefinition = field.data.boundaryDefinition_
+		
+		mesh = field.data.mesh_
+		
+		# Define the required parameters and set the boundary values
+		nFacesBound = np.size(mesh.connectivityData.owner_)					   \
+					  - np.size(mesh.connectivityData.neighbour_)
+		
+		noComponents = self.noComponents_
+		
+		# Initialize the boundary array
+		boundaryValues = np.empty((noComponents, nFacesBound), dtype = float)
+		
+		# Loop over the boundaries, and assign corresponding values,
+		# depending on the boundary definition array
+		for boundaryPatch in mesh.connectivityData.boundary_:
+			boundaryName = boundaryPatch[0]
+			boundaryType = (boundaryDefinition[boundaryName])[0]
+			
+			setBoundaryValuesFunc = eval(									   \
+				"self.setBoundaryValuesFunctionsGrad." + boundaryType)
+			
+			setBoundaryValuesFunc											   \
+				(mesh, noComponents, cellValues, field, boundaryValues, boundaryPatch, boundaryDefinition)
+		
+		return boundaryValues
+	
+	
+	class writeBoundaryValuesFunctions:
+		
+		def fixedValue(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			fValue = (boundaryDefinition[boundaryName])[1]
+			
+			file.write(2*'\t' + 'value' + 2*'\t' + 'uniform ')
+			file.write('(')
+			# X component
+			file.write(str(fValue[0]) + ' ')
+			# Y component
+			file.write(str(fValue[1]) + ' ')
+			# Z component
+			file.write(str(fValue[2]) + ');\n')
+		
+		def empty(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			None
+			
+		def fixedGradient(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			fGradient = (boundaryDefinition[boundaryName])[1]
+			
+			file.write(2*'\t' + 'gradient' + 2*'\t' + 'uniform ')
+			file.write('(')
+			# X component
+			file.write(str(fGradient[0]) + ' ')
+			# Y component
+			file.write(str(fGradient[1]) + ' ')
+			# Z component
+			file.write(str(fGradient[2]) + ');\n')
+			
+		def calculated(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			for i in mesh.connectivityData.boundary_:
+				
+				if (i[0] == boundaryName):
+					boundaryPatch = i
+					break
+					
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			nFaces = boundaryPatch[2]
+			startFace = boundaryPatch[3]
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+			
+			file.write(2*'\t' + 'value' + 2*'\t' + 'nonuniform List<vector>\n')
+			file.write(str(nFaces) + '\n')
+			file.write('(\n')
+			
+			for faceIndex in range(startFace, startFace + nFaces):
+				
+				boundaryFaceIndex = faceIndex - nInternalFaces
+				
+				file.write('(')
+				# X component
+				file.write(str(boundaryValues[0][boundaryFaceIndex]) + ' ')
+				# Y component
+				file.write(str(boundaryValues[1][boundaryFaceIndex]) + ' ')
+				# Z component
+				file.write(str(boundaryValues[2][boundaryFaceIndex]) + ')\n')
+			
+			file.write(');\n')
+			
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/volField/volVectorField/volVectorField_py.py b/foamPython/src/OpenFOAM/fields/volField/volVectorField/volVectorField_py.py
new file mode 100644
index 0000000000000000000000000000000000000000..39627dc37d27c530deda25875398ce3f8f1d2691
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/volField/volVectorField/volVectorField_py.py
@@ -0,0 +1,259 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Volume vector field class, pure Python.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.OpenFOAM.fields.volField.volField import *
+
+
+
+class volVectorField(volField):
+	
+	fieldTypeName_ = "vector"
+	className_ = "volVectorField"
+	noComponents_ = 3
+	componentsNames_ = ("x", "y", "z")
+	
+	# Constructor used to calculate the gradient of a volField
+	@classmethod
+	def grad(self, field):
+		
+		mesh = field.data.mesh_
+		
+		fieldName = "grad" + field.data.fieldName_
+		
+		boundaryDefinitionField = field.data.boundaryDefinition_
+		boundaryDefinition = dict.fromkeys(boundaryDefinitionField)
+		
+		for fieldPatch in boundaryDefinitionField:
+			 
+			if (boundaryDefinitionField[fieldPatch][0] == 'empty'):
+				boundaryDefinition[fieldPatch] = boundaryDefinitionField[fieldPatch]
+			else:
+				boundaryDefinition[fieldPatch] = 							   \
+					('calculated', None)
+		
+		# Set cell values
+		cellValues		= self.setCellValuesGrad(field)
+		
+		# Set boundary values
+		boundaryValues  = self.setBoundaryValuesInitialGrad(cellValues, field)
+		
+		return self(mesh, fieldName, boundaryValues, cellValues, boundaryDefinition)
+	
+	
+	@classmethod
+	def setCellValuesGrad(self, field):
+		
+		mesh = field.data.mesh_
+		
+		C = mesh.geometryData.C_
+		Cf = mesh.geometryData.Cf_
+		Sf = mesh.geometryData.Sf_
+		V = mesh.geometryData.V_
+		
+		# Number of components of the field to make a gradient of
+		noComponentsField = field.noComponents_
+		
+		# The tensor rank of the result is 1 higher than the field rank
+		noComponentsGrad = field.noComponents_ * 3
+		
+		# Total number of cells
+		nCells = mesh.read("geometryData.meshSize")
+		# Number of internal faces
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		# Total number of faces
+		nFacesTot = np.size(mesh.connectivityData.owner_)
+		
+		cellValues = field.data.cellValues_
+		
+		gradField = np.zeros((noComponentsGrad, nCells), dtype = float)
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		interpCellValues = np.empty(noComponentsField, dtype = float)
+		
+		# For all internal faces
+		for faceIndex in range(nInternalFaces):
+				
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			absPf = np.linalg.norm(Cf[faceIndex] - C[ownIndex])
+			absNf = np.linalg.norm(Cf[faceIndex] - C[neiIndex])
+			
+			f = absNf / (absPf + absNf)
+			
+			# For all field components, interpolate value to face
+			for cmpt in range(noComponentsField):
+							
+				interpCellValues[cmpt] = 									   \
+					f * cellValues[cmpt][ownIndex] + 						   \
+					(1 - f) * cellValues[cmpt][neiIndex]
+			
+			# For all grad components
+			cmptGrad = 0
+			# For all Sf components (3)
+			for cmptSf in range(3):
+				# For all field components
+				for cmptField in range(noComponentsField):
+					# int_V gradField dV = sum_f Sf * field_f > owner
+					gradField[cmptGrad][ownIndex] +=						   \
+						interpCellValues[cmptField] * 						   \
+						Sf[faceIndex][cmptSf]
+					
+					# int_V gradField dV = - sum_f Sf * field_f > neighbour
+					gradField[cmptGrad][neiIndex] -=						   \
+						interpCellValues[cmptField] * 						   \
+						Sf[faceIndex][cmptSf]
+					
+					# Increment gradient result component counter
+					cmptGrad += 1
+		
+		boundaryValues = field.data.boundaryValues_
+		
+		# For all boundary faces
+		for faceIndex in range(nInternalFaces, nFacesTot):
+			
+			boundaryCellIndex = owner[faceIndex]
+			
+			boundaryFaceIndex = faceIndex - nInternalFaces
+			
+			# For all grad components
+			cmptGrad = 0
+			# For all Sf components (3)
+			for cmptSf in range(3):
+				# For all field components
+				for cmptField in range(noComponentsField):
+					# int_V gradField dV = sum_f Sf * field_f
+					gradField[cmptGrad][boundaryCellIndex] +=				   \
+						boundaryValues[cmptField][boundaryFaceIndex] *		   \
+						Sf[faceIndex][cmptSf]
+						
+					# Increment gradient result component counter
+					cmptGrad += 1
+			
+		
+		# Divide all cell values by the volume of the specific cell. The
+		# previously calculated gradient values were actually volume integrals
+		for cmptGrad in range(noComponentsGrad):
+			gradField[cmptGrad] /= V
+		
+		
+		return gradField
+	
+	
+	@classmethod
+	def setBoundaryValuesInitialGrad(self, cellValues, field):
+		
+		boundaryDefinition = field.data.boundaryDefinition_
+		
+		mesh = field.data.mesh_
+		
+		# Define the required parameters and set the boundary values
+		nFacesBound = np.size(mesh.connectivityData.owner_)					   \
+					  - np.size(mesh.connectivityData.neighbour_)
+		
+		noComponents = self.noComponents_
+		
+		# Initialize the boundary array
+		boundaryValues = np.empty((noComponents, nFacesBound), dtype = float)
+		
+		# Loop over the boundaries, and assign corresponding values,
+		# depending on the boundary definition array
+		for boundaryPatch in mesh.connectivityData.boundary_:
+			boundaryName = boundaryPatch[0]
+			boundaryType = (boundaryDefinition[boundaryName])[0]
+			
+			setBoundaryValuesFunc = eval(									   \
+				"self.setBoundaryValuesFunctionsGrad." + boundaryType)
+			
+			setBoundaryValuesFunc											   \
+				(mesh, noComponents, cellValues, field, boundaryValues, boundaryPatch, boundaryDefinition)
+		
+		return boundaryValues
+	
+	
+	class writeBoundaryValuesFunctions:
+		
+		def fixedValue(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			fValue = (boundaryDefinition[boundaryName])[1]
+			
+			file.write(2*'\t' + 'value' + 2*'\t' + 'uniform ')
+			file.write('(')
+			# X component
+			file.write(str(fValue[0]) + ' ')
+			# Y component
+			file.write(str(fValue[1]) + ' ')
+			# Z component
+			file.write(str(fValue[2]) + ');\n')
+		
+		def empty(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			None
+			
+		def fixedGradient(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			fGradient = (boundaryDefinition[boundaryName])[1]
+			
+			file.write(2*'\t' + 'gradient' + 2*'\t' + 'uniform ')
+			file.write('(')
+			# X component
+			file.write(str(fGradient[0]) + ' ')
+			# Y component
+			file.write(str(fGradient[1]) + ' ')
+			# Z component
+			file.write(str(fGradient[2]) + ');\n')
+			
+		def calculated(file, boundaryName, boundaryDefinition, mesh, boundaryValues):
+			
+			for i in mesh.connectivityData.boundary_:
+				
+				if (i[0] == boundaryName):
+					boundaryPatch = i
+					break
+					
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			nFaces = boundaryPatch[2]
+			startFace = boundaryPatch[3]
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+			
+			file.write(2*'\t' + 'value' + 2*'\t' + 'nonuniform List<vector>\n')
+			file.write(str(nFaces) + '\n')
+			file.write('(\n')
+			
+			for faceIndex in range(startFace, startFace + nFaces):
+				
+				boundaryFaceIndex = faceIndex - nInternalFaces
+				
+				file.write('(')
+				# X component
+				file.write(str(boundaryValues[0][boundaryFaceIndex]) + ' ')
+				# Y component
+				file.write(str(boundaryValues[1][boundaryFaceIndex]) + ' ')
+				# Z component
+				file.write(str(boundaryValues[2][boundaryFaceIndex]) + ')\n')
+			
+			file.write(');\n')
+			
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/OpenFOAM/fields/writeHeader.py b/foamPython/src/OpenFOAM/fields/writeHeader.py
new file mode 100644
index 0000000000000000000000000000000000000000..0899afa31732ca7b267589cf074290d7b4a27350
--- /dev/null
+++ b/foamPython/src/OpenFOAM/fields/writeHeader.py
@@ -0,0 +1,35 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Header string global variable used for writing the field results
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+HEADER =																	   \
+'/*---------------------------------------------------------------------------*\\\n\
+ ####                 ####     #   #             |\n\
+ #                    #  #     ##  #             | FoamPython\n\
+ ##  #### ####  ##### #### # # #   #### #### ### | \n\
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |\n\
+ #   #### ##### # # # #    #    #  #  # #### # # |\n\
+\*---------------------------------------------------------------------------*/\n\
+FoamFile\n\
+{\n\
+    version     2.0;\n\
+    format      ascii;\n\
+'
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/cfdTools/general/constrainHbyA/constrainHbyA.py b/foamPython/src/finiteVolume/cfdTools/general/constrainHbyA/constrainHbyA.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ecd9e2a805fd75af81cb666b9c04810aa952b2b
--- /dev/null
+++ b/foamPython/src/finiteVolume/cfdTools/general/constrainHbyA/constrainHbyA.py
@@ -0,0 +1,56 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Function to constrain HbyA in pressure-velocity coupling
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+def constrainHbyA(HbyA, U):
+	
+	boundaryDefinition_U = U.data.boundaryDefinition_
+	
+	# """---------------- Go through the faces "by hand" ----------------------"""
+	
+	# meshBoundary = U.data.mesh_.connectivityData.boundary_
+	
+	# noComponents = HbyA.noComponents_
+	
+	# nInternalFaces = np.size(U.data.mesh_.connectivityData.neighbour_)
+	
+	# for patch in meshBoundary:
+		
+		# boundaryName = patch[0]
+		
+		# if ( (boundaryDefinition_p[boundaryName])[0] == 'fixedGradient' ):
+			
+			# startFace = patch[3]
+			# nFaces = patch[2]
+			
+			# for cmpt in range(noComponents):
+				
+				# for faceIndex in range(startFace, startFace + nFaces):
+					
+					# boundaryFaceIndex = faceIndex - nInternalFaces
+					
+					# HbyA.data.boundaryValues_[cmpt][boundaryFaceIndex] =	   \
+						# U.data.boundaryValues_[cmpt][boundaryFaceIndex]
+
+	# """----------------------------------------------------------------------"""
+	
+	"""---------------- Set velocity boundary conditions --------------------"""
+	
+	HbyA.updateBoundaryDefinition(boundaryDefinition_U)
+	HbyA.setBoundaryValues(boundaryDefinition_U)
diff --git a/foamPython/src/finiteVolume/cfdTools/include.py b/foamPython/src/finiteVolume/cfdTools/include.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7037eaf4aa255e0fb8a3a23683b14507a61db40
--- /dev/null
+++ b/foamPython/src/finiteVolume/cfdTools/include.py
@@ -0,0 +1,38 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Include file for the cfdTools folder.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import os
+
+# Save current directory
+oldDir = os.getcwd()
+
+# Get the directory of this file
+os.chdir(os.path.dirname(os.path.abspath(__file__)))
+
+# The location of continuityErrs code
+continuityErrs = open("incompressible/continuityErrs.py").read()
+
+# Go back to the old directory
+os.chdir(oldDir)
+
+# Import the constrainHbyA function
+from src.finiteVolume.cfdTools.general.constrainHbyA.constrainHbyA import *
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/cfdTools/incompressible/continuityErrs.py b/foamPython/src/finiteVolume/cfdTools/incompressible/continuityErrs.py
new file mode 100644
index 0000000000000000000000000000000000000000..5da6c81de8574116509568548d3cf2663eebf4a0
--- /dev/null
+++ b/foamPython/src/finiteVolume/cfdTools/incompressible/continuityErrs.py
@@ -0,0 +1,27 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Code for continuity error-checking
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+contErr = volScalarField.div(phi)
+
+sumLocalContErr = deltaT * np.sum(np.absolute(contErr.data.cellValues_[0]) * np.average(mesh.geometryData.V_))
+
+globalContErr = deltaT * np.sum(contErr.data.cellValues_[0] * np.average(mesh.geometryData.V_))
+
+cumulativeContErr += globalContErr
+
+print("Time step continuity errors: sum local = " + str(sumLocalContErr) + ", global = " + str(globalContErr) + ", cumulative = " + str(cumulativeContErr))
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvMatrix.py b/foamPython/src/finiteVolume/fvMatrices/fvMatrix.py
new file mode 100644
index 0000000000000000000000000000000000000000..a3de4b009079f8dc25de495a10ab501aeb557c3e
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvMatrix.py
@@ -0,0 +1,518 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    A special matrix type and solver, designed for finite volume
+    solutions of scalar equations.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+from src.finiteVolume.fvSolution.include import *
+from src.OpenFOAM.fields.include import *
+
+class fvMatrix():
+	
+	solversDict_ =															   \
+	{																		   \
+		"GaussSeidel" : GaussSeidel.solveMatrix,							   \
+		"PointJacobi" : PointJacobi.solveMatrix								   \
+	}
+	
+	class fvMatrixData:
+		def __init__(self):
+			
+			mesh_		= None # fvMesh object
+			
+			psi_		= None # Field to be solved
+			
+			source_		= None # Source term
+			
+			# Matrix coefficients
+			lower_		= None
+			diag_		= None
+			upper_		= None
+		
+			rowStart_	= None # Used to enable looping over cells/matrix rows
+
+	"""------------------------- Constructors -------------------------------"""	
+	# Main constructor
+	def __init__(self, psi, source, lower, diag, upper, rowStart):
+		
+		self.data = self.fvMatrixData()
+		
+		self.data.mesh_ 				= psi.data.mesh_
+		
+		self.data.psi_					= psi
+		
+		self.data.source_				= source
+		
+		self.data.lower_				= lower
+		self.data.diag_					= diag
+		self.data.upper_				= upper
+		self.data.rowStart_				= rowStart
+	
+	# Constructor to define the fvMatrix using string evaluation and a 
+	# dictionary of operators which have been implemented within fvm or fvc
+	@classmethod
+	def construct(self, psi, operator, scheme, fvVariables):
+		
+		rowStart						= self.defineRowStart(psi.data.mesh_)
+		
+		source, lower, diag, upper		=									   \
+			self.defineMatrix(psi, operator, scheme, fvVariables)
+			
+		return self(psi, source, lower, diag, upper, rowStart)
+
+
+	"""------------- Matrix components definition functions -----------------"""
+	
+	# Calls the right solver based on the "solver" input specified for the field
+	
+	def solve(self, fvSolutionParameters):
+		
+		solversDict = self.solversDict_
+		
+		try:
+			solver = fvSolutionParameters['solver']
+		except:
+			raise RuntimeError(												   \
+				"No solver specified for field " + equationName)
+		
+		# Assign the appropriate solver to the solverFunction variable
+		try:
+			solverFunction = solversDict[solver]
+		except:
+			raise RuntimeError(												   \
+				"Solver " + solver + " has not been implemented!")
+		
+		print() # Output new line
+		
+		# Run the appropriate solver
+		solverFunction(self, fvSolutionParameters)
+	
+	
+	def relax(self, fvSolutionParameters):
+		
+		alpha = 1.0
+		
+		try:
+			alpha = fvSolutionParameters['impUR']
+		except:
+			None
+		
+		lower = self.data.lower_
+		diag = self.data.diag_
+		upper = self.data.upper_
+		source = self.data.source_
+		
+		# Store the current unrelaxed diagonal for use in updating the source
+		diagOld = np.copy(diag)
+		
+		mesh = self.data.psi_.data.mesh_
+		nCells = mesh.geometryData.meshSize_
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		noComponents = self.data.psi_.noComponents_
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		# # sumMagOffDiag
+		# sumOff = np.zeros((noComponents, nCells), dtype=float)
+
+		# for cmpt in range(noComponents):
+			
+			# for faceIndex in range(nInternalFaces):
+				
+				# ownIndex = owner[faceIndex]
+				# neiIndex = neighbour[faceIndex]
+				
+				# sumOff[cmpt][ownIndex] += abs(upper[cmpt][faceIndex])
+				# sumOff[cmpt][neiIndex] += abs(lower[cmpt][faceIndex])
+		
+		# Ensure the matrix is diagonally dominant and that the diagonal coefficient
+		# is positive
+		# for cmpt in range(noComponents):
+			
+			# for cellIndex in range(nCells):
+				
+				# diag[cmpt][cellIndex] = max(abs(diag[cmpt][cellIndex]), sumOff[cmpt][cellIndex])
+		
+		# ... then relax
+		diag /= alpha
+		
+		# Finally add the relaxation contribution to the source
+		source += (diag - diagOld) * self.data.psi_.data.cellValues_
+	
+	def deRelax(self, fvSolutionParameters):
+		
+		alpha = 1.0
+		
+		try:
+			alpha = fvSolutionParameters['impUR']
+		except:
+			None
+		
+		self.data.diag_ *= alpha
+	
+	
+	def residual(self):
+		
+		noComponents = self.data.psi_.noComponents_
+		
+		residual = np.empty(noComponents, dtype = float)
+		normFactor = np.full(noComponents, None)
+		
+		for cmpt in range(noComponents):
+			
+			residual[cmpt], normFactor[cmpt] =								   \
+				fvSolution.calculateResidual(self, cmpt, normFactor[cmpt])
+		
+		return residual
+	
+	
+	def defineRowStart(mesh):
+		
+		# Total number of cells
+		nCells = mesh.read("geometryData.meshSize")
+		# Number of internal faces
+		nInternalFaces = np.size(mesh.read("connectivityData.neighbour"))
+		
+		owner = mesh.connectivityData.owner_
+		
+		# This algorithm is copied from OpenFOAM
+		rowStart = np.full(nCells + 1, nInternalFaces, dtype = int)
+		
+		rowStart[0] = 0
+		nOwnStart = 0
+		i = 1
+		
+		for faceIndex in range(nInternalFaces):
+			curOwn = owner[faceIndex]
+
+			if (curOwn > nOwnStart):
+				while (i <= curOwn):
+					rowStart[i] = faceIndex
+					i += 1
+
+				nOwnStart = curOwn
+		
+		return rowStart
+	
+	
+	"""------------------------ General functions ---------------------------"""
+	
+	def A(self):
+		# Returns volScalarField object with zero gradient boundary condition -
+		# first-order extrapolation to the boundary
+		
+		mesh = self.data.psi_.data.mesh_
+
+		psiName = self.data.psi_.data.fieldName_
+		AName = "A" + psiName
+		
+		# Define zero gradient boundary definition for all of the patches for diag
+		psiBoundaryDefinition = self.data.psi_.data.boundaryDefinition_
+		ABoundaryDefinition = dict.fromkeys(psiBoundaryDefinition)
+		
+		for psiPatch in psiBoundaryDefinition:
+			 
+			if (psiBoundaryDefinition[psiPatch][0] == 'empty'):
+				ABoundaryDefinition[psiPatch] = psiBoundaryDefinition[psiPatch]
+			else:
+				ABoundaryDefinition[psiPatch] = 							   \
+					('fixedGradient', np.zeros(1, dtype=float))
+		
+		AInternalField = np.zeros(1, dtype=float)
+		
+		AClass = volScalarField
+			
+		AVolField = AClass.initialize(AName, mesh, ABoundaryDefinition, AInternalField)
+		
+		# Copy diag reciprocal diag values to A field
+		AVolField.data.cellValues_[0] = np.copy(self.data.diag_[0])
+		# Convert to [s] by multiplying with volume of cells
+		AVolField.data.cellValues_[0] *= mesh.geometryData.V_
+		
+		AVolField.setBoundaryValues(ABoundaryDefinition)
+		
+		return AVolField
+	
+	
+	# Function to return the reciprocal diagonal part of the A matrix as a
+	# volField, 1/A
+	def rA(self):
+		# Returns volScalarField object with zero gradient boundary condition -
+		# first-order extrapolation to the boundary
+		
+		mesh = self.data.psi_.data.mesh_
+
+		psiName = self.data.psi_.data.fieldName_
+		rAName = "rA" + psiName
+		
+		# Define zero gradient boundary definition for all of the patches for diag
+		psiBoundaryDefinition = self.data.psi_.data.boundaryDefinition_
+		rABoundaryDefinition = dict.fromkeys(psiBoundaryDefinition)
+		
+		for psiPatch in psiBoundaryDefinition:
+			 
+			if (psiBoundaryDefinition[psiPatch][0] == 'empty'):
+				rABoundaryDefinition[psiPatch] = psiBoundaryDefinition[psiPatch]
+			else:
+				rABoundaryDefinition[psiPatch] = 							   \
+					('fixedGradient', np.zeros(1, dtype=float))
+		
+		rAInternalField = np.zeros(1, dtype=float)
+		
+		rAClass = volScalarField
+			
+		rAVolField = rAClass.initialize(rAName, mesh, rABoundaryDefinition, rAInternalField)
+		
+		# Copy diag reciprocal diag values to rA field
+		rAVolField.data.cellValues_[0] = np.copy(np.reciprocal(self.data.diag_[0]))
+		# Convert to [s] by multiplying with volume of cells
+		rAVolField.data.cellValues_[0] *= mesh.geometryData.V_
+		
+		rAVolField.setBoundaryValues(rABoundaryDefinition)
+		
+		return rAVolField
+	
+	
+	# Function to return the H() part of the matrix
+	def H(self):
+		# H = b - sum_N a_n * psi_n
+		# Zero gradient boundary condition - first-order extrapolation to the
+		# boundary
+		
+		mesh = self.data.psi_.data.mesh_
+		
+		noComponents = self.data.psi_.noComponents_
+		
+		# Define boundary definition for all of the patches for HField
+		psiBoundaryDefinition = self.data.psi_.data.boundaryDefinition_
+		HBoundaryDefinition = dict.fromkeys(psiBoundaryDefinition)
+		
+		for psiPatch in psiBoundaryDefinition:
+			 
+			if (psiBoundaryDefinition[psiPatch][0] == 'empty'):
+				HBoundaryDefinition[psiPatch] = psiBoundaryDefinition[psiPatch]
+			else:# (psiBoundaryDefinition[psiPatch][0] == 'fixedValue'):
+				HBoundaryDefinition[psiPatch] = 							   \
+					('fixedGradient', np.zeros(noComponents, dtype=float))
+			# else:
+				# raise RuntimeError("H for velocity boundary condition " +	   \
+					# psiPatch + " has not yet been implemented!")
+		
+		HInternalField = np.zeros(noComponents, dtype=float)
+		
+		# Choose the appropriate resulting class, depending on the class of the field
+		HClass = eval(self.data.psi_.className_)
+		
+		HVolField = HClass.initialize("H", mesh, HBoundaryDefinition, HInternalField)
+		
+		# Set the H values to b
+		HCellValues = np.copy(self.data.source_)
+		
+		# Subtract a_n * psi_n from H values
+		psi = self.data.psi_.data.cellValues_
+		
+		source = self.data.source_
+		
+		lower = self.data.lower_
+		upper = self.data.upper_
+
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		# For all components
+		for cmpt in range(noComponents):
+			
+			# For all faces
+			for faceIndex in range(neighbour.size):
+				
+				ownIndex = owner[faceIndex]
+				neiIndex = neighbour[faceIndex]
+				
+				HCellValues[cmpt][ownIndex] -=								   \
+					upper[cmpt][faceIndex] * psi[cmpt][neiIndex]
+				
+				HCellValues[cmpt][neiIndex] -=								   \
+					lower[cmpt][faceIndex] * psi[cmpt][ownIndex]
+		
+		# Convert to [m/s2] by dividing by volume of cells
+		for cmpt in range(noComponents):
+			HCellValues[cmpt] /= mesh.geometryData.V_
+		
+		HVolField.data.cellValues_ = HCellValues
+		HVolField.setBoundaryValues(HBoundaryDefinition)
+		
+		return HVolField
+	
+	
+	# Prints the contributions and values for a specified cell and component
+	def cellAnalysis(self, cellNo, cmpt):
+		
+		mesh = self.data.mesh_
+		
+		psi = self.data.psi_.data.cellValues_[cmpt]
+		
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		source = self.data.source_[cmpt]
+		
+		lower = self.data.lower_[cmpt]
+		diag = self.data.diag_[cmpt]
+		upper = self.data.upper_[cmpt]
+		
+		resultArray = np.empty(0, dtype = object)
+		
+		counter = 0
+		
+		for faceIndex in range(nInternalFaces):
+			
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			if (ownIndex == cellNo):
+				
+				resultArray = np.append(resultArray, None)
+				
+				resultArray[counter] = np.array([upper[faceIndex], neiIndex])
+				
+				counter += 1
+			
+			if (neiIndex == cellNo):
+				
+				resultArray = np.append(resultArray, None)
+				resultArray[counter] = np.array([lower[faceIndex], ownIndex])
+				
+				counter += 1
+		
+		# Printing out results
+		print()
+		
+		for nei in range(np.size(resultArray)):
+			
+			print("an to cell " + str(resultArray[nei][1]) + " is " +		   \
+				str(resultArray[nei][0]) + ". The value in the cell is " + str(psi[int(resultArray[nei][1] + 1e-10)]))
+		
+		print("ap in cell " + str(cellNo) + " is " + str(diag[cellNo]) +	   \
+			". The values in the cell is " + str(psi[cellNo]))
+		
+		print("b in cell " + str(cellNo) + " is " + str(source[cellNo]))
+		
+		print()
+
+	"""------------------------ Defining operators --------------------------"""
+	def __add__(self, other): # + operator
+		
+		if (self.data.mesh_ != other.data.mesh_):
+			raise RuntimeError(												   \
+				"The matrices are not assembled using the same mesh!")
+		
+		if (self.data.psi_ != other.data.psi_):
+			raise RuntimeError(												   \
+				"The matrices are not assembled using the same field!")
+		
+		# Copy
+		mesh = self.data.mesh_
+		
+		psi = self.data.psi_
+		
+		rowStart = self.data.rowStart_
+		
+		# Add up the source and A matrix contributions
+		source = np.copy(self.data.source_) + np.copy(other.data.source_)
+		
+		lower = np.copy(self.data.lower_) + np.copy(other.data.lower_)
+		diag = np.copy(self.data.diag_) + np.copy(other.data.diag_)
+		upper = np.copy(self.data.upper_) + np.copy(other.data.upper_)
+
+		return fvMatrix(psi, source, lower, diag, upper, rowStart)
+		
+	def __sub__(self, other): # - operator
+		
+		if (self.data.mesh_ != other.data.mesh_):
+			raise RuntimeError(												   \
+				"The matrices are not assembled using the same mesh!")
+		
+		if (self.data.psi_ != other.data.psi_):
+			raise RuntimeError(												   \
+				"The matrices are not assembled using the same field!")
+				
+		# Copy
+		mesh = self.data.mesh_
+		
+		psi = self.data.psi_
+		
+		rowStart = self.data.rowStart_
+		
+		# Subtract the source and A matrix contributions
+		source = np.copy(self.data.source_) - np.copy(other.data.source_)
+		
+		lower = np.copy(self.data.lower_) - np.copy(other.data.lower_)
+		diag = np.copy(self.data.diag_) - np.copy(other.data.diag_)
+		upper = np.copy(self.data.upper_) - np.copy(other.data.upper_)
+		
+		return fvMatrix(psi, source, lower, diag, upper, rowStart)
+	
+	def __neg__(self): # - operator in front of a matrix
+						
+		# Copy
+		mesh = self.data.mesh_
+		
+		psi = self.data.psi_
+		
+		rowStart = self.data.rowStart_
+		
+		# Subtract the source and A matrix contributions
+		source = - np.copy(self.data.source_)
+		
+		lower = - np.copy(self.data.lower_)
+		diag = - np.copy(self.data.diag_)
+		upper = - np.copy(self.data.upper_)
+		
+		return fvMatrix(psi, source, lower, diag, upper, rowStart)
+	
+	def __eq__(self, other): # == operator
+		
+		if (self.data.mesh_ != other.data.mesh_):
+			raise RuntimeError(												   \
+				"The matrices are not assembled using the same mesh!")
+		
+		if (self.data.psi_ != other.data.psi_):
+			raise RuntimeError(												   \
+				"The matrices are not assembled using the same field!")
+				
+		# Copy
+		mesh = self.data.mesh_
+		
+		psi = self.data.psi_
+		
+		rowStart = self.data.rowStart_
+		
+		# Subtract the source and A matrix contributions
+		source = np.copy(self.data.source_) + np.copy(other.data.source_)
+		
+		lower = np.copy(self.data.lower_) - np.copy(other.data.lower_)
+		diag = np.copy(self.data.diag_) - np.copy(other.data.diag_)
+		upper = np.copy(self.data.upper_) - np.copy(other.data.upper_)
+		
+		return fvMatrix(psi, source, lower, diag, upper, rowStart)
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvc/fvSchemes/div/div.py b/foamPython/src/finiteVolume/fvMatrices/fvc/fvSchemes/div/div.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c42798d17e7ea1a6c2990b88a791f0c3a99399f
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvc/fvSchemes/div/div.py
@@ -0,0 +1,113 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the divergence contributions for fvc
+    
+\*---------------------------------------------------------------------------*/
+"""
+import numpy as np
+
+class divClass:
+	
+	class div():
+		
+		@classmethod
+		def linear(self, mesh, psi, fvVariables):
+			
+			field = fvVariables[0]
+			
+			C = mesh.geometryData.C_
+			Cf = mesh.geometryData.Cf_
+			Sf = mesh.geometryData.Sf_
+			
+			# Number of components of the field to make a gradient of
+			noComponentsField = field.noComponents_
+			
+			# The tensor rank of the result is 1 lower than the field rank
+			noComponentsDiv = int(field.noComponents_ / 3 + 1e-10)
+			
+			if (noComponentsDiv != 1):
+				raise RuntimeError("Wrong or not yet implemented field type for div!")
+			
+			# Total number of cells
+			nCells = mesh.read("geometryData.meshSize")
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			nFacesTot = np.size(mesh.connectivityData.owner_)
+			
+			cellValues = field.data.cellValues_
+			boundaryValues = field.data.boundaryValues_
+			
+			source = np.zeros((noComponentsDiv, nCells), dtype = float)
+			
+			lower = np.zeros((noComponentsDiv, nInternalFaces), dtype = float)
+			diag = np.zeros((noComponentsDiv, nCells), dtype = float)
+			upper = np.zeros((noComponentsDiv, nInternalFaces), dtype = float)
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+			
+			faceValue = np.empty(noComponentsField, dtype = float)
+			
+			# For all internal faces
+			for faceIndex in range(nInternalFaces):
+					
+				ownIndex = owner[faceIndex]
+				neiIndex = neighbour[faceIndex]
+				
+				absPf = np.linalg.norm(Cf[faceIndex] - C[ownIndex])
+				absNf = np.linalg.norm(Cf[faceIndex] - C[neiIndex])
+				
+				f = absNf / (absPf + absNf)
+				
+				# For all field components, interpolate value to face
+				for cmptField in range(noComponentsField):
+								
+					faceValue[cmptField] = 									   \
+						f * cellValues[cmptField][ownIndex] + 				   \
+						(1 - f) * cellValues[cmptField][neiIndex]
+				
+				# Works only for a scalar equation (div of a vector field)!!!
+				
+				# Div component
+				cmptDiv = 0
+				
+				source[cmptDiv][ownIndex] += 								   \
+					np.dot(faceValue, Sf[faceIndex])
+					
+				source[cmptDiv][neiIndex] -= 								   \
+					np.dot(faceValue, Sf[faceIndex])
+					
+			for faceIndex in range(nInternalFaces, nFacesTot):
+				
+				boundaryFaceIndex = faceIndex - nInternalFaces
+				
+				boundaryCellIndex = owner[faceIndex]
+				
+				# Works only for a scalar equation (div of a vector field)!!!
+				
+				# Div component
+				cmptDiv = 0
+				
+				for cmptField in range(noComponentsField):
+					faceValue[cmptField] = boundaryValues[cmptField][boundaryFaceIndex]
+				
+				source[cmptDiv][boundaryCellIndex] +=						   \
+					np.dot(faceValue, Sf[faceIndex])
+					
+			return source, lower, diag, upper
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvc/fvSchemes/fvSchemes.py b/foamPython/src/finiteVolume/fvMatrices/fvc/fvSchemes/fvSchemes.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f24c98e66932d26d9a61364ccb662ca708c6c7c
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvc/fvSchemes/fvSchemes.py
@@ -0,0 +1,33 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the functions needed to assemble the explicit fvMatrix,
+    fvc
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.finiteVolume.fvMatrices.fvc.fvSchemes.grad.grad import *
+from src.finiteVolume.fvMatrices.fvc.fvSchemes.div.div import *
+
+class fvSchemes():
+	
+	# Dictionary of implemented operators
+	operatorsDict_ = {														   \
+					  "grad": gradClass.grad,								   \
+					  "div": divClass.div									   \
+					 }
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvc/fvSchemes/grad/boundaryContributions.py b/foamPython/src/finiteVolume/fvMatrices/fvc/fvSchemes/grad/boundaryContributions.py
new file mode 100644
index 0000000000000000000000000000000000000000..52c7003a017e9cdbfb76c176f696ee6ee117213d
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvc/fvSchemes/grad/boundaryContributions.py
@@ -0,0 +1,88 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Boundary contributions for the fvc gradient operator
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+class gradBoundaryContributions:
+	
+	class boundaryContributions:
+		
+		def fixedValue(mesh, psi, boundaryPatch, source, diag, field):
+			
+			boundaryName = boundaryPatch[0]
+			nFaces = boundaryPatch[2]
+			startFace = boundaryPatch[3]
+			
+			# Values of psi on the boundary
+			fieldBoundaryValues = field.data.boundaryValues_
+			
+			C = mesh.geometryData.C_
+			Cf = mesh.geometryData.Cf_
+			Sf = mesh.geometryData.Sf_
+			
+			# Number of components of the field to make a gradient of
+			noComponentsField = field.noComponents_
+			
+			# The tensor rank of the result is 1 higher than the field rank
+			noComponentsGrad = field.noComponents_ * 3
+			
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			owner = mesh.connectivityData.owner_
+			
+			# For all boundary faces
+			for faceIndex in range(startFace, startFace + nFaces):
+						
+				boundaryCellIndex = owner[faceIndex]
+						   
+				boundaryIndex = faceIndex - nInternalFaces
+				
+				# For all grad components
+				cmptGrad = 0
+				# For all Sf components (3)
+				for cmptSf in range(3):
+					# For all psi components
+					for cmptField in range(noComponentsField):
+						# b_owner = Sf * field_b
+						source[cmptGrad][boundaryCellIndex] +=				   \
+							fieldBoundaryValues[cmptField][boundaryIndex] *    \
+							Sf[faceIndex][cmptSf]
+						
+						# Increment gradient result component counter
+						cmptGrad += 1
+			
+			return source, diag
+			
+			
+		def empty(mesh, psi, boundaryPatch, source, diag, field):
+			
+			None
+			
+			return source, diag
+			
+		# Both the fixedGradient and fixedValue boundary contributions are
+		# assembled using existing calculated boundary values
+		#(psi.data.boundaryValues_), so it is essentially the same function:
+		
+		fixedGradient = fixedValue
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvc/fvSchemes/grad/grad.py b/foamPython/src/finiteVolume/fvMatrices/fvc/fvSchemes/grad/grad.py
new file mode 100644
index 0000000000000000000000000000000000000000..b84144aeb7b277d49b1ed399f49075f4235faf65
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvc/fvSchemes/grad/grad.py
@@ -0,0 +1,114 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the gradient contributions for fvc
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.finiteVolume.fvMatrices.fvc.fvSchemes.grad.boundaryContributions import *
+
+class gradClass:
+	
+	class grad(gradBoundaryContributions):
+		
+		@classmethod
+		def linear(self, mesh, psi, fvVariables):
+			
+			field = fvVariables[0]
+			
+			C = mesh.geometryData.C_
+			Cf = mesh.geometryData.Cf_
+			Sf = mesh.geometryData.Sf_
+			
+			# Number of components of the field to make a gradient of
+			noComponentsField = field.noComponents_
+			
+			# The tensor rank of the result is 1 higher than the field rank
+			noComponentsGrad = field.noComponents_ * 3
+			
+			# Total number of cells
+			nCells = mesh.read("geometryData.meshSize")
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			cellValues = field.data.cellValues_
+			
+			source = np.zeros((noComponentsGrad, nCells), dtype = float)
+			
+			lower = np.zeros((noComponentsGrad, nInternalFaces), dtype = float)
+			diag = np.zeros((noComponentsGrad, nCells), dtype = float)
+			upper = np.zeros((noComponentsGrad, nInternalFaces), dtype = float)
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+			
+			interpCellValues = np.empty(noComponentsField, dtype = float)
+			
+			# For all internal faces
+			for faceIndex in range(nInternalFaces):
+					
+				ownIndex = owner[faceIndex]
+				neiIndex = neighbour[faceIndex]
+				
+				absPf = np.linalg.norm(Cf[faceIndex] - C[ownIndex])
+				absNf = np.linalg.norm(Cf[faceIndex] - C[neiIndex])
+				
+				f = absNf / (absPf + absNf)
+				
+				# For all field components, interpolate value to face
+				for cmpt in range(noComponentsField):
+								
+					interpCellValues[cmpt] = 								   \
+						f * cellValues[cmpt][ownIndex] + 					   \
+						(1 - f) * cellValues[cmpt][neiIndex]
+				
+				# For all grad components
+				cmptGrad = 0
+				# For all Sf components (3)
+				for cmptSf in range(3):
+					# For all field components
+					for cmptField in range(noComponentsField):
+						# b_owner = Sf * field_f
+						source[cmptGrad][ownIndex] +=						   \
+							interpCellValues[cmptField] * 					   \
+							Sf[faceIndex][cmptSf]
+						
+						# b_neighbour = - Sf * field_f
+						source[cmptGrad][neiIndex] -=						   \
+							interpCellValues[cmptField] * 					   \
+							Sf[faceIndex][cmptSf]
+						
+						# Increment gradient result component counter
+						cmptGrad += 1
+			
+			# Add boundary contributions. The loop goes through the boundary
+			# patches and, depending on the boundaryType, defines the source and
+			# diagonal contributions by calling the appropriate functions
+			for boundaryPatch in mesh.connectivityData.boundary_:
+				boundaryName = boundaryPatch[0]
+				boundaryType = (field.data.boundaryDefinition_[boundaryName])[0]
+				
+				boundaryContributionsFunctionString =						   \
+					"self.boundaryContributions."			   \
+					+ boundaryType											   \
+					+ "(mesh, psi, boundaryPatch, source, diag, field)"
+				
+				source, diag = eval(boundaryContributionsFunctionString)
+			
+			return source, lower, diag, upper
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvc/fvc.py b/foamPython/src/finiteVolume/fvMatrices/fvc/fvc.py
new file mode 100644
index 0000000000000000000000000000000000000000..90b03e8875ffda6c0567c90b72954e430696febc
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvc/fvc.py
@@ -0,0 +1,53 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Finite volume matrix contributions, explicit
+    
+    Note: fvc in OpenFOAM returns a field, as opposed to this implementation,
+    where fvc returns an fvMatrix
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.finiteVolume.fvMatrices.fvc.fvSchemes.fvSchemes import *
+from src.finiteVolume.fvMatrices.fvMatrix import *
+
+class fvc(fvMatrix, fvSchemes):
+
+	"""------------- Matrix components definition functions ----------------"""
+	
+	@classmethod
+	def defineMatrix(self, psi, operator, scheme, fvVariables):
+		
+		mesh = psi.data.mesh_
+		
+		# The dictionary of operator classes
+		operatorsDict = self.operatorsDict_
+		
+		# Assign the appropriate operator class
+		try:
+			operatorClass = operatorsDict[operator]
+		except:
+			raise RuntimeError("Operator " + operator + " is not implemented!")
+		
+		# Assign the appropriate function inside the operator class
+		operatorSchemeFunc = eval("operatorClass." + scheme)
+		
+		# Call the appropriate function
+		source, lower, diag, upper = operatorSchemeFunc(mesh, psi, fvVariables)
+		
+		return source, lower, diag, upper
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/ddt/ddt.py b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/ddt/ddt.py
new file mode 100644
index 0000000000000000000000000000000000000000..8299913ac87290efc061f1420fcea734e2c1bb32
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/ddt/ddt.py
@@ -0,0 +1,64 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the temporal contributions for fvm
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+class ddtClass:
+	
+	class ddt:
+		
+		def Euler(mesh, psi, fvVariables):
+			
+			deltaT = fvVariables[0]
+			cellVolumes = mesh.geometryData.V_
+			cellValues = psi.data.cellValues_
+			
+			noComponents = psi.noComponents_
+			
+			# Total number of cells
+			nCells = mesh.read("geometryData.meshSize")
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			source = np.zeros((noComponents, nCells), dtype = float)
+			
+			lower = np.zeros((noComponents, nInternalFaces), dtype = float)
+			diag = np.zeros((noComponents, nCells), dtype = float)
+			upper = np.zeros((noComponents, nInternalFaces), dtype = float)
+			
+			"""SOURCE DEFINITION"""
+			# b = V * psi_old / deltaT
+			for cmpt in range(noComponents):
+				for cellIndex in range(nCells):
+					source[cmpt][cellIndex] = 								   \
+						cellVolumes[cellIndex] * cellValues[cmpt][cellIndex]   \
+						/ deltaT
+						
+			"""A MATRIX DEFINITION"""
+			# ap = V / deltaT
+			for cmpt in range(noComponents):
+				for cellIndex in range(nCells):
+					diag[cmpt][cellIndex] = 								   \
+						cellVolumes[cellIndex] 								   \
+						/ deltaT
+			
+			return source, lower, diag, upper
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/div/boundaryContributions.py b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/div/boundaryContributions.py
new file mode 100644
index 0000000000000000000000000000000000000000..70d8106d776679618a75a62590808e70f25ff846
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/div/boundaryContributions.py
@@ -0,0 +1,108 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Boundary contributions for the fvm divergence operator
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+class divBoundaryContributions:
+	
+	class boundaryContributions:
+		
+		def fixedValue(mesh, psi, boundaryPatch, source, diag, phi):
+			
+			boundaryName = boundaryPatch[0]
+			nFaces = boundaryPatch[2]
+			startFace = boundaryPatch[3]
+			
+			# Values of psi on the boundary
+			psiBoundaryValues = psi.data.boundaryValues_
+			
+			noComponents = psi.noComponents_
+			
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			for cmpt in range(noComponents):
+			
+				for faceIndex in range(startFace, startFace + nFaces):
+					
+					boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+						   
+					boundaryIndex = faceIndex - nInternalFaces
+					
+					# This holds only for a single component phi field!
+					
+					# b = F * psi_b
+					source[cmpt][boundaryCellIndex] += 				  		   \
+						phi[0][faceIndex] * psiBoundaryValues[cmpt][boundaryIndex]
+			
+			return source, diag
+			
+			
+		def empty(mesh, psi, boundaryPatch, source, diag, phi):
+			
+			None
+			
+			return source, diag
+			
+			
+		def fixedGradient(mesh, psi, boundaryPatch, source, diag, phi):
+			
+			boundaryName = boundaryPatch[0]
+			nFaces = boundaryPatch[2]
+			startFace = boundaryPatch[3]
+			
+			# Cell centres
+			C = mesh.geometryData.C_
+			# Face centres
+			Cf = mesh.geometryData.Cf_
+			
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			# The value of the gradient
+			gradPsi_b = (psi.data.boundaryDefinition_[boundaryName])[1]
+			
+			noComponents = psi.noComponents_
+			
+			for cmpt in range(noComponents):
+			
+				for faceIndex in range(startFace, startFace + nFaces):
+					
+					boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+						   
+					boundaryIndex = faceIndex - nInternalFaces
+					
+					# This holds only for a single component phi field!
+					
+					# ap = F
+					diag[cmpt][boundaryCellIndex] += 						   \
+						 phi[0][faceIndex]
+					
+					# b = F * |d_b| * gradPsi_b
+					
+					d_b = np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex])
+					
+					source[cmpt][boundaryCellIndex] += 				 	 	   \
+						 phi[0][faceIndex] * d_b * gradPsi_b[cmpt]
+						
+			
+			return source, diag
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/div/div.py b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/div/div.py
new file mode 100644
index 0000000000000000000000000000000000000000..9364f358d5255c7da5257804830fb7649bf50c60
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/div/div.py
@@ -0,0 +1,94 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the divergence contributions for fvm
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+from src.finiteVolume.fvMatrices.fvm.fvSchemes.div.boundaryContributions import *
+
+class divClass:
+	
+	class div(divBoundaryContributions):
+		
+		@classmethod
+		def upwind(self, mesh, psi, fvVariables):
+			
+			phi = fvVariables[0].data.faceValues_
+			Sf = mesh.geometryData.Sf_
+			C = mesh.geometryData.C_
+			
+			noComponents = psi.noComponents_
+			
+			# Total number of cells
+			nCells = mesh.read("geometryData.meshSize")
+			# Total number of faces
+			nFacesTot = np.size(mesh.connectivityData.owner_)
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			source = np.zeros((noComponents, nCells), dtype = float)
+			
+			lower = np.zeros((noComponents, nInternalFaces), dtype = float)
+			diag = np.zeros((noComponents, nCells), dtype = float)
+			upper = np.zeros((noComponents, nInternalFaces), dtype = float)
+					 
+					 
+			"""A MATRIX DEFINITION"""
+			# Add internal faces contributions
+			for cmpt in range(noComponents):
+				
+				for faceIndex in range(nInternalFaces):
+				
+					# aP_owner = max(F, 0)
+					# aN_owner = min(F, 0)
+					# aP_neighbour = max(-F, 0)
+					# aN_neighbour = min(-F, 0)
+					
+					cellIndexP = mesh.connectivityData.owner_[faceIndex]
+					cellIndexN = mesh.connectivityData.neighbour_[faceIndex]
+					
+					# This holds only for a single component phi field!
+					
+					lower[cmpt][faceIndex] += min(-phi[0][faceIndex], 0)
+					
+					diag[cmpt][cellIndexP] += max(phi[0][faceIndex], 0)
+					
+					diag[cmpt][cellIndexN] += max(-phi[0][faceIndex], 0)
+					
+					upper[cmpt][faceIndex] += min(phi[0][faceIndex], 0)
+			
+			
+			# Add boundary contributions. The loop goes through the boundary
+			# patches and, depending on the boundaryType, defines the source and
+			# diagonal contributions by calling the appropriate functions
+			for boundaryPatch in mesh.connectivityData.boundary_:
+				boundaryName = boundaryPatch[0]
+				boundaryType = (psi.data.boundaryDefinition_[boundaryName])[0]
+				
+				boundaryContributionsFunctionString =						   \
+					"self.boundaryContributions."			   \
+					+ boundaryType											   \
+					+ "(mesh, psi, boundaryPatch, source, diag, phi)"
+				
+				source, diag = eval(boundaryContributionsFunctionString)
+					
+			return source, lower, diag, upper
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/fvSchemes.py b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/fvSchemes.py
new file mode 100644
index 0000000000000000000000000000000000000000..638a8098f2fab96edfd747a1be684f3bca60e1ec
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/fvSchemes.py
@@ -0,0 +1,35 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the functions needed to assemble the implicit fvMatrix,
+    fvm
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.finiteVolume.fvMatrices.fvm.fvSchemes.laplacian.laplacian import *
+from src.finiteVolume.fvMatrices.fvm.fvSchemes.ddt.ddt import *
+from src.finiteVolume.fvMatrices.fvm.fvSchemes.div.div import *
+
+class fvSchemes(laplacianClass, ddtClass, divClass):
+	
+	# Dictionary of implemented operators
+	operatorsDict_ = {														   \
+					  "laplacian": laplacianClass.laplacian,				   \
+					  "ddt" : ddtClass.ddt,									   \
+					  "div" : divClass.div									   \
+					 }
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/boundaryContributions.py b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/boundaryContributions.py
new file mode 100644
index 0000000000000000000000000000000000000000..874e5842745cbd05ff1037683f28e1547a4fa04c
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/boundaryContributions.py
@@ -0,0 +1,201 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Boundary contributions for the fvm laplacian operator, pure Python.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+class laplacianBoundaryContributions:
+	
+	class boundaryContributions:
+		
+		class scalarGamma:
+		
+			def fixedValue(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				# Cell centres
+				C = mesh.geometryData.C_
+				# Face centres
+				Cf = mesh.geometryData.Cf_
+				# Values of psi on the boundary
+				psiBoundaryValues = psi.data.boundaryValues_
+				
+				noComponents = psi.noComponents_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+						
+						# ap = - gamma * |Sf| / |db|
+						diag[cmpt][boundaryCellIndex] += 					   	   \
+							- gamma * magSf[faceIndex] / 					       \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex]))
+						
+						# b = - gamma * |Sf| / |db| * psi_b
+						source[cmpt][boundaryCellIndex] += 				  		   \
+							- gamma * magSf[faceIndex] / 						   \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex])) \
+							* psiBoundaryValues[cmpt][boundaryIndex]
+				
+				return source, diag
+				
+				
+			def empty(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				None
+				
+				return source, diag
+				
+				
+			def fixedGradient(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				# The value of the gradient
+				gradPsi_b = (psi.data.boundaryDefinition_[boundaryName])[1]
+				
+				noComponents = psi.noComponents_
+				
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+
+						# b = - gamma * |Sf| * gradPsi_b
+						source[cmpt][boundaryCellIndex] += 				 	 	   \
+							 - gamma * magSf[faceIndex] * gradPsi_b[cmpt]
+							
+				return source, diag
+				
+		class volScalarFieldGamma:
+
+			def fixedValue(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				gammaBoundaryValues = gamma.data.boundaryValues_
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				# Cell centres
+				C = mesh.geometryData.C_
+				# Face centres
+				Cf = mesh.geometryData.Cf_
+				# Values of psi on the boundary
+				psiBoundaryValues = psi.data.boundaryValues_
+				
+				noComponents = psi.noComponents_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				# Gamma is a volScalarField, so only one component
+				gammaCmpt = 0
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+						
+						# ap = - gamma * |Sf| / |db|
+						diag[cmpt][boundaryCellIndex] += 				   	   \
+							- gammaBoundaryValues[gammaCmpt][boundaryIndex] *  \
+							magSf[faceIndex] / 							       \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex]))
+						
+						# b = - gamma * |Sf| / |db| * psi_b
+						source[cmpt][boundaryCellIndex] += 					   \
+							- gammaBoundaryValues[gammaCmpt][boundaryIndex] *  \
+							magSf[faceIndex] / 								   \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex])) \
+							* psiBoundaryValues[cmpt][boundaryIndex]
+				
+				return source, diag
+				
+				
+			def empty(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				None
+				
+				return source, diag
+				
+				
+			def fixedGradient(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				gammaBoundaryValues = gamma.data.boundaryValues_
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				# The value of the gradient
+				gradPsi_b = (psi.data.boundaryDefinition_[boundaryName])[1]
+				
+				noComponents = psi.noComponents_
+				
+				# Gamma is a volScalarField, so only one component
+				gammaCmpt = 0
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+
+						# b = - gamma * |Sf| * gradPsi_b
+						source[cmpt][boundaryCellIndex] += 			 	 	   \
+							 - gammaBoundaryValues[gammaCmpt][boundaryIndex] * \
+							 magSf[faceIndex] * gradPsi_b
+							
+				return source, diag
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/boundaryContributions.py.orig b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/boundaryContributions.py.orig
new file mode 100644
index 0000000000000000000000000000000000000000..874e5842745cbd05ff1037683f28e1547a4fa04c
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/boundaryContributions.py.orig
@@ -0,0 +1,201 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Boundary contributions for the fvm laplacian operator, pure Python.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+class laplacianBoundaryContributions:
+	
+	class boundaryContributions:
+		
+		class scalarGamma:
+		
+			def fixedValue(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				# Cell centres
+				C = mesh.geometryData.C_
+				# Face centres
+				Cf = mesh.geometryData.Cf_
+				# Values of psi on the boundary
+				psiBoundaryValues = psi.data.boundaryValues_
+				
+				noComponents = psi.noComponents_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+						
+						# ap = - gamma * |Sf| / |db|
+						diag[cmpt][boundaryCellIndex] += 					   	   \
+							- gamma * magSf[faceIndex] / 					       \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex]))
+						
+						# b = - gamma * |Sf| / |db| * psi_b
+						source[cmpt][boundaryCellIndex] += 				  		   \
+							- gamma * magSf[faceIndex] / 						   \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex])) \
+							* psiBoundaryValues[cmpt][boundaryIndex]
+				
+				return source, diag
+				
+				
+			def empty(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				None
+				
+				return source, diag
+				
+				
+			def fixedGradient(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				# The value of the gradient
+				gradPsi_b = (psi.data.boundaryDefinition_[boundaryName])[1]
+				
+				noComponents = psi.noComponents_
+				
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+
+						# b = - gamma * |Sf| * gradPsi_b
+						source[cmpt][boundaryCellIndex] += 				 	 	   \
+							 - gamma * magSf[faceIndex] * gradPsi_b[cmpt]
+							
+				return source, diag
+				
+		class volScalarFieldGamma:
+
+			def fixedValue(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				gammaBoundaryValues = gamma.data.boundaryValues_
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				# Cell centres
+				C = mesh.geometryData.C_
+				# Face centres
+				Cf = mesh.geometryData.Cf_
+				# Values of psi on the boundary
+				psiBoundaryValues = psi.data.boundaryValues_
+				
+				noComponents = psi.noComponents_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				# Gamma is a volScalarField, so only one component
+				gammaCmpt = 0
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+						
+						# ap = - gamma * |Sf| / |db|
+						diag[cmpt][boundaryCellIndex] += 				   	   \
+							- gammaBoundaryValues[gammaCmpt][boundaryIndex] *  \
+							magSf[faceIndex] / 							       \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex]))
+						
+						# b = - gamma * |Sf| / |db| * psi_b
+						source[cmpt][boundaryCellIndex] += 					   \
+							- gammaBoundaryValues[gammaCmpt][boundaryIndex] *  \
+							magSf[faceIndex] / 								   \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex])) \
+							* psiBoundaryValues[cmpt][boundaryIndex]
+				
+				return source, diag
+				
+				
+			def empty(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				None
+				
+				return source, diag
+				
+				
+			def fixedGradient(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				gammaBoundaryValues = gamma.data.boundaryValues_
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				# The value of the gradient
+				gradPsi_b = (psi.data.boundaryDefinition_[boundaryName])[1]
+				
+				noComponents = psi.noComponents_
+				
+				# Gamma is a volScalarField, so only one component
+				gammaCmpt = 0
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+
+						# b = - gamma * |Sf| * gradPsi_b
+						source[cmpt][boundaryCellIndex] += 			 	 	   \
+							 - gammaBoundaryValues[gammaCmpt][boundaryIndex] * \
+							 magSf[faceIndex] * gradPsi_b
+							
+				return source, diag
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/boundaryContributions_cy.pyx b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/boundaryContributions_cy.pyx
new file mode 100644
index 0000000000000000000000000000000000000000..f0879fd5ccfc8e28b8119cfa9e9851191a465831
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/boundaryContributions_cy.pyx
@@ -0,0 +1,258 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Boundary contributions for the fvm laplacian operator, with Cython.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+cimport numpy as np
+
+cdef int noComponents
+cdef int nCells
+cdef int nInternalFaces
+cdef int cmpt
+cdef int faceIndex
+cdef int boundaryCellIndex
+cdef int boundaryIndex
+cdef int cellIndexP
+cdef int cellIndexN
+cdef int nFaces
+cdef int startFace
+cdef int gammaCmpt
+
+
+class laplacianBoundaryContributions:
+	
+	class boundaryContributions:
+		
+		class scalarGamma:
+		
+			def fixedValue(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				"""----------------- Cython declarations --------------------"""
+				cdef np.ndarray[np.float_t, ndim=2] psiBoundaryValues
+				
+				cdef np.ndarray[np.float_t, ndim=1] magSf
+				cdef np.ndarray[np.float_t, ndim=2] C
+				cdef np.ndarray[np.float_t, ndim=2] Cf
+				cdef np.ndarray[np.int_t, ndim=1] owner
+				"""----------------------------------------------------------"""
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				# Cell centres
+				C = mesh.geometryData.C_
+				# Face centres
+				Cf = mesh.geometryData.Cf_
+				# Values of psi on the boundary
+				psiBoundaryValues = psi.data.boundaryValues_
+				
+				noComponents = psi.noComponents_
+				
+				owner = mesh.connectivityData.owner_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = owner[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+						
+						# ap = - gamma * |Sf| / |db|
+						diag[cmpt][boundaryCellIndex] += 					   	   \
+							- gamma * magSf[faceIndex] / 					       \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex]))
+						
+						# b = - gamma * |Sf| / |db| * psi_b
+						source[cmpt][boundaryCellIndex] += 				  		   \
+							- gamma * magSf[faceIndex] / 						   \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex])) \
+							* psiBoundaryValues[cmpt][boundaryIndex]
+				
+				return source, diag
+				
+				
+			def empty(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				None
+				
+				return source, diag
+				
+				
+			def fixedGradient(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				"""----------------- Cython declarations --------------------"""
+				cdef np.ndarray[np.float_t, ndim=1] gradPsi_b
+				
+				cdef np.ndarray[np.float_t, ndim=1] magSf
+				cdef np.ndarray[np.int_t, ndim=1] owner
+				"""----------------------------------------------------------"""
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				# The value of the gradient
+				gradPsi_b = (psi.data.boundaryDefinition_[boundaryName])[1]
+				
+				noComponents = psi.noComponents_
+				
+				owner = mesh.connectivityData.owner_
+				
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = owner[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+
+						# b = - gamma * |Sf| * gradPsi_b
+						source[cmpt][boundaryCellIndex] += 				 	 	   \
+							 - gamma * magSf[faceIndex] * gradPsi_b[cmpt]
+							
+				return source, diag
+				
+		class volScalarFieldGamma:
+
+			def fixedValue(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				"""----------------- Cython declarations --------------------"""
+				cdef np.ndarray[np.float_t, ndim=2] gammaBoundaryValues
+				
+				cdef np.ndarray[np.float_t, ndim=1] magSf
+				cdef np.ndarray[np.float_t, ndim=2] C
+				cdef np.ndarray[np.float_t, ndim=2] Cf
+				cdef np.ndarray[np.int_t, ndim=1] owner
+				"""----------------------------------------------------------"""
+				
+				gammaBoundaryValues = gamma.data.boundaryValues_
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				# Cell centres
+				C = mesh.geometryData.C_
+				# Face centres
+				Cf = mesh.geometryData.Cf_
+				# Values of psi on the boundary
+				psiBoundaryValues = psi.data.boundaryValues_
+				
+				noComponents = psi.noComponents_
+				
+				owner = mesh.connectivityData.owner_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				# Gamma is a volScalarField, so only one component
+				gammaCmpt = 0
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = owner[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+						
+						# ap = - gamma * |Sf| / |db|
+						diag[cmpt][boundaryCellIndex] += 				   	   \
+							- gammaBoundaryValues[gammaCmpt][boundaryIndex] *  \
+							magSf[faceIndex] / 							       \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex]))
+						
+						# b = - gamma * |Sf| / |db| * psi_b
+						source[cmpt][boundaryCellIndex] += 					   \
+							- gammaBoundaryValues[gammaCmpt][boundaryIndex] *  \
+							magSf[faceIndex] / 								   \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex])) \
+							* psiBoundaryValues[cmpt][boundaryIndex]
+				
+				return source, diag
+				
+				
+			def empty(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				None
+				
+				return source, diag
+				
+				
+			def fixedGradient(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				"""----------------- Cython declarations --------------------"""
+				cdef np.ndarray[np.float_t, ndim=1] gradPsi_b
+				cdef np.ndarray[np.float_t, ndim=2] gammaBoundaryValues
+				
+#				cdef np.ndarray[np.float_t, ndim=2] source
+				cdef np.ndarray[np.float_t, ndim=1] magSf
+				cdef np.ndarray[np.int_t, ndim=1] owner
+				"""----------------------------------------------------------"""
+				
+				gammaBoundaryValues = gamma.data.boundaryValues_
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				# The value of the gradient
+				gradPsi_b = (psi.data.boundaryDefinition_[boundaryName])[1]
+				
+				noComponents = psi.noComponents_
+				
+				owner = mesh.connectivityData.owner_
+				
+				# Gamma is a volScalarField, so only one component
+				gammaCmpt = 0
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = owner[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+
+						# b = - gamma * |Sf| * gradPsi_b
+						source[cmpt][boundaryCellIndex] += 			 	 	   \
+							 - gammaBoundaryValues[gammaCmpt][boundaryIndex] * \
+							 magSf[faceIndex] * gradPsi_b
+							
+				return source, diag
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/boundaryContributions_py.py b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/boundaryContributions_py.py
new file mode 100644
index 0000000000000000000000000000000000000000..874e5842745cbd05ff1037683f28e1547a4fa04c
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/boundaryContributions_py.py
@@ -0,0 +1,201 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Boundary contributions for the fvm laplacian operator, pure Python.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+class laplacianBoundaryContributions:
+	
+	class boundaryContributions:
+		
+		class scalarGamma:
+		
+			def fixedValue(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				# Cell centres
+				C = mesh.geometryData.C_
+				# Face centres
+				Cf = mesh.geometryData.Cf_
+				# Values of psi on the boundary
+				psiBoundaryValues = psi.data.boundaryValues_
+				
+				noComponents = psi.noComponents_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+						
+						# ap = - gamma * |Sf| / |db|
+						diag[cmpt][boundaryCellIndex] += 					   	   \
+							- gamma * magSf[faceIndex] / 					       \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex]))
+						
+						# b = - gamma * |Sf| / |db| * psi_b
+						source[cmpt][boundaryCellIndex] += 				  		   \
+							- gamma * magSf[faceIndex] / 						   \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex])) \
+							* psiBoundaryValues[cmpt][boundaryIndex]
+				
+				return source, diag
+				
+				
+			def empty(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				None
+				
+				return source, diag
+				
+				
+			def fixedGradient(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				# The value of the gradient
+				gradPsi_b = (psi.data.boundaryDefinition_[boundaryName])[1]
+				
+				noComponents = psi.noComponents_
+				
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+
+						# b = - gamma * |Sf| * gradPsi_b
+						source[cmpt][boundaryCellIndex] += 				 	 	   \
+							 - gamma * magSf[faceIndex] * gradPsi_b[cmpt]
+							
+				return source, diag
+				
+		class volScalarFieldGamma:
+
+			def fixedValue(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				gammaBoundaryValues = gamma.data.boundaryValues_
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				# Cell centres
+				C = mesh.geometryData.C_
+				# Face centres
+				Cf = mesh.geometryData.Cf_
+				# Values of psi on the boundary
+				psiBoundaryValues = psi.data.boundaryValues_
+				
+				noComponents = psi.noComponents_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				# Gamma is a volScalarField, so only one component
+				gammaCmpt = 0
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+						
+						# ap = - gamma * |Sf| / |db|
+						diag[cmpt][boundaryCellIndex] += 				   	   \
+							- gammaBoundaryValues[gammaCmpt][boundaryIndex] *  \
+							magSf[faceIndex] / 							       \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex]))
+						
+						# b = - gamma * |Sf| / |db| * psi_b
+						source[cmpt][boundaryCellIndex] += 					   \
+							- gammaBoundaryValues[gammaCmpt][boundaryIndex] *  \
+							magSf[faceIndex] / 								   \
+							(np.linalg.norm(Cf[faceIndex] - C[boundaryCellIndex])) \
+							* psiBoundaryValues[cmpt][boundaryIndex]
+				
+				return source, diag
+				
+				
+			def empty(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				None
+				
+				return source, diag
+				
+				
+			def fixedGradient(mesh, psi, boundaryPatch, source, diag, gamma):
+				
+				gammaBoundaryValues = gamma.data.boundaryValues_
+				
+				boundaryName = boundaryPatch[0]
+				nFaces = boundaryPatch[2]
+				startFace = boundaryPatch[3]
+				
+				# Face area magnitudes
+				magSf = mesh.geometryData.magSf_
+				
+				# Number of internal faces
+				nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+				
+				# The value of the gradient
+				gradPsi_b = (psi.data.boundaryDefinition_[boundaryName])[1]
+				
+				noComponents = psi.noComponents_
+				
+				# Gamma is a volScalarField, so only one component
+				gammaCmpt = 0
+				for cmpt in range(noComponents):
+				
+					for faceIndex in range(startFace, startFace + nFaces):
+						
+						boundaryCellIndex = mesh.connectivityData.owner_[faceIndex]
+							   
+						boundaryIndex = faceIndex - nInternalFaces
+
+						# b = - gamma * |Sf| * gradPsi_b
+						source[cmpt][boundaryCellIndex] += 			 	 	   \
+							 - gammaBoundaryValues[gammaCmpt][boundaryIndex] * \
+							 magSf[faceIndex] * gradPsi_b
+							
+				return source, diag
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/laplacian.py b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/laplacian.py
new file mode 100644
index 0000000000000000000000000000000000000000..5007916f8b758627e9baa24984679a6227b3a9ed
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/laplacian.py
@@ -0,0 +1,197 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the laplacian contributions for fvm, pure Python.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+from src.OpenFOAM.fields.volField.volScalarField.volScalarField import *
+from src.finiteVolume.fvMatrices.fvm.fvSchemes.laplacian.boundaryContributions import *
+
+class laplacianClass:
+	
+	class laplacian(laplacianBoundaryContributions):
+		
+		@classmethod
+		def linearOrthogonal(self, mesh, psi, fvVariables):
+			
+			gamma = fvVariables[0]
+			
+			# Check for type of diffusivity and call the appropriate function
+			if (type(gamma) == int or type(gamma) == float):
+				
+				source, lower, diag, upper = self.linearOrthogonalScalar(mesh, psi, fvVariables)
+				
+			elif (type(gamma) == volScalarField):
+				
+				source, lower, diag, upper = self.linearOrthogonalVolScalarField(mesh, psi, fvVariables)
+				
+			else:
+				
+				raise RuntimeError(											   \
+				"Laplacian with a diffusivity of type " + str(type(gamma)) +   \
+				" has not been implemented!")
+			
+			return source, lower, diag, upper
+			
+			
+		@classmethod
+		def linearOrthogonalScalar(self, mesh, psi, fvVariables):
+			
+			gamma = fvVariables[0]
+			magSf = mesh.geometryData.magSf_
+			C = mesh.geometryData.C_
+			
+			noComponents = psi.noComponents_
+			
+			# Total number of cells
+			nCells = mesh.read("geometryData.meshSize")
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			source = np.zeros((noComponents, nCells), dtype = float)
+			
+			lower = np.zeros((noComponents, nInternalFaces), dtype = float)
+			diag = np.zeros((noComponents, nCells), dtype = float)
+			upper = np.zeros((noComponents, nInternalFaces), dtype = float)
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+					 
+			"""A MATRIX DEFINITION"""
+			# Add internal faces contributions
+			for cmpt in range(noComponents):
+				
+				for faceIndex in range(nInternalFaces):
+				
+					# aP = - sum_N gamma * |Sf| / |df|
+					# aN = gamma * |Sf| / |df|
+					
+					cellIndexP = owner[faceIndex]
+					cellIndexN = neighbour[faceIndex]
+					
+					lower[cmpt][faceIndex] = gamma * magSf[faceIndex] 		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+							   
+					diag[cmpt][cellIndexP] -= gamma * magSf[faceIndex]		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					diag[cmpt][cellIndexN] -= gamma * magSf[faceIndex]		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					upper[cmpt][faceIndex] = gamma * magSf[faceIndex]		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+			
+			
+			# Add boundary contributions. The loop goes through the boundary
+			# patches and, depending on the boundaryType, defines the source and
+			# diagonal contributions by calling the appropriate functions
+			for boundaryPatch in mesh.connectivityData.boundary_:
+				boundaryName = boundaryPatch[0]
+				boundaryType = (psi.data.boundaryDefinition_[boundaryName])[0]
+				
+				boundaryContributionsFunctionString =						   \
+					"self.boundaryContributions.scalarGamma."				   \
+					+ boundaryType											   \
+					+ "(mesh, psi, boundaryPatch, source, diag, gamma)"
+				
+				source, diag = eval(boundaryContributionsFunctionString)
+					
+			return source, lower, diag, upper
+			
+		@classmethod
+		def linearOrthogonalVolScalarField(self, mesh, psi, fvVariables):
+			
+			gamma = fvVariables[0]
+			gammaValues = gamma.data.cellValues_
+			
+			magSf = mesh.geometryData.magSf_
+			C = mesh.geometryData.C_
+			Cf = mesh.geometryData.Cf_
+			
+			noComponents = psi.noComponents_
+			
+			# Total number of cells
+			nCells = mesh.read("geometryData.meshSize")
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			source = np.zeros((noComponents, nCells), dtype = float)
+			
+			lower = np.zeros((noComponents, nInternalFaces), dtype = float)
+			diag = np.zeros((noComponents, nCells), dtype = float)
+			upper = np.zeros((noComponents, nInternalFaces), dtype = float)
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+			
+			# Gamma is a volScalarField, so only one component
+			gammaCmpt = 0
+			
+			"""A MATRIX DEFINITION"""
+			# Add internal faces contributions
+			for cmpt in range(noComponents):
+				
+				for faceIndex in range(nInternalFaces):
+					
+					cellIndexP = owner[faceIndex]
+					cellIndexN = neighbour[faceIndex]
+					
+					absPf = np.linalg.norm(Cf[faceIndex] - C[cellIndexP])
+					absNf = np.linalg.norm(Cf[faceIndex] - C[cellIndexN])
+					
+					f = absNf / (absPf + absNf)
+
+					gammaInterpF = 											   \
+						f * gammaValues[gammaCmpt][cellIndexP] + 			   \
+						(1 - f) * gammaValues[gammaCmpt][cellIndexN]
+				
+					# aP = - sum_N gamma * |Sf| / |df|
+					# aN = gamma * |Sf| / |df|
+					
+					lower[cmpt][faceIndex] = gammaInterpF * magSf[faceIndex]   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+							   
+					diag[cmpt][cellIndexP] -= gammaInterpF * magSf[faceIndex]  \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					diag[cmpt][cellIndexN] -= gammaInterpF * magSf[faceIndex]  \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					upper[cmpt][faceIndex] = gammaInterpF * magSf[faceIndex]   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+			
+			
+			# Add boundary contributions. The loop goes through the boundary
+			# patches and, depending on the boundaryType, defines the source and
+			# diagonal contributions by calling the appropriate functions
+			for boundaryPatch in mesh.connectivityData.boundary_:
+				boundaryName = boundaryPatch[0]
+				boundaryType = (psi.data.boundaryDefinition_[boundaryName])[0]
+				
+				boundaryContributionsFunctionString =						   \
+					"self.boundaryContributions.volScalarFieldGamma."		   \
+					+ boundaryType											   \
+					+ "(mesh, psi, boundaryPatch, source, diag, gamma)"
+				
+				source, diag = eval(boundaryContributionsFunctionString)
+					
+			return source, lower, diag, upper
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/laplacian.py.orig b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/laplacian.py.orig
new file mode 100644
index 0000000000000000000000000000000000000000..5007916f8b758627e9baa24984679a6227b3a9ed
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/laplacian.py.orig
@@ -0,0 +1,197 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the laplacian contributions for fvm, pure Python.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+from src.OpenFOAM.fields.volField.volScalarField.volScalarField import *
+from src.finiteVolume.fvMatrices.fvm.fvSchemes.laplacian.boundaryContributions import *
+
+class laplacianClass:
+	
+	class laplacian(laplacianBoundaryContributions):
+		
+		@classmethod
+		def linearOrthogonal(self, mesh, psi, fvVariables):
+			
+			gamma = fvVariables[0]
+			
+			# Check for type of diffusivity and call the appropriate function
+			if (type(gamma) == int or type(gamma) == float):
+				
+				source, lower, diag, upper = self.linearOrthogonalScalar(mesh, psi, fvVariables)
+				
+			elif (type(gamma) == volScalarField):
+				
+				source, lower, diag, upper = self.linearOrthogonalVolScalarField(mesh, psi, fvVariables)
+				
+			else:
+				
+				raise RuntimeError(											   \
+				"Laplacian with a diffusivity of type " + str(type(gamma)) +   \
+				" has not been implemented!")
+			
+			return source, lower, diag, upper
+			
+			
+		@classmethod
+		def linearOrthogonalScalar(self, mesh, psi, fvVariables):
+			
+			gamma = fvVariables[0]
+			magSf = mesh.geometryData.magSf_
+			C = mesh.geometryData.C_
+			
+			noComponents = psi.noComponents_
+			
+			# Total number of cells
+			nCells = mesh.read("geometryData.meshSize")
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			source = np.zeros((noComponents, nCells), dtype = float)
+			
+			lower = np.zeros((noComponents, nInternalFaces), dtype = float)
+			diag = np.zeros((noComponents, nCells), dtype = float)
+			upper = np.zeros((noComponents, nInternalFaces), dtype = float)
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+					 
+			"""A MATRIX DEFINITION"""
+			# Add internal faces contributions
+			for cmpt in range(noComponents):
+				
+				for faceIndex in range(nInternalFaces):
+				
+					# aP = - sum_N gamma * |Sf| / |df|
+					# aN = gamma * |Sf| / |df|
+					
+					cellIndexP = owner[faceIndex]
+					cellIndexN = neighbour[faceIndex]
+					
+					lower[cmpt][faceIndex] = gamma * magSf[faceIndex] 		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+							   
+					diag[cmpt][cellIndexP] -= gamma * magSf[faceIndex]		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					diag[cmpt][cellIndexN] -= gamma * magSf[faceIndex]		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					upper[cmpt][faceIndex] = gamma * magSf[faceIndex]		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+			
+			
+			# Add boundary contributions. The loop goes through the boundary
+			# patches and, depending on the boundaryType, defines the source and
+			# diagonal contributions by calling the appropriate functions
+			for boundaryPatch in mesh.connectivityData.boundary_:
+				boundaryName = boundaryPatch[0]
+				boundaryType = (psi.data.boundaryDefinition_[boundaryName])[0]
+				
+				boundaryContributionsFunctionString =						   \
+					"self.boundaryContributions.scalarGamma."				   \
+					+ boundaryType											   \
+					+ "(mesh, psi, boundaryPatch, source, diag, gamma)"
+				
+				source, diag = eval(boundaryContributionsFunctionString)
+					
+			return source, lower, diag, upper
+			
+		@classmethod
+		def linearOrthogonalVolScalarField(self, mesh, psi, fvVariables):
+			
+			gamma = fvVariables[0]
+			gammaValues = gamma.data.cellValues_
+			
+			magSf = mesh.geometryData.magSf_
+			C = mesh.geometryData.C_
+			Cf = mesh.geometryData.Cf_
+			
+			noComponents = psi.noComponents_
+			
+			# Total number of cells
+			nCells = mesh.read("geometryData.meshSize")
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			source = np.zeros((noComponents, nCells), dtype = float)
+			
+			lower = np.zeros((noComponents, nInternalFaces), dtype = float)
+			diag = np.zeros((noComponents, nCells), dtype = float)
+			upper = np.zeros((noComponents, nInternalFaces), dtype = float)
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+			
+			# Gamma is a volScalarField, so only one component
+			gammaCmpt = 0
+			
+			"""A MATRIX DEFINITION"""
+			# Add internal faces contributions
+			for cmpt in range(noComponents):
+				
+				for faceIndex in range(nInternalFaces):
+					
+					cellIndexP = owner[faceIndex]
+					cellIndexN = neighbour[faceIndex]
+					
+					absPf = np.linalg.norm(Cf[faceIndex] - C[cellIndexP])
+					absNf = np.linalg.norm(Cf[faceIndex] - C[cellIndexN])
+					
+					f = absNf / (absPf + absNf)
+
+					gammaInterpF = 											   \
+						f * gammaValues[gammaCmpt][cellIndexP] + 			   \
+						(1 - f) * gammaValues[gammaCmpt][cellIndexN]
+				
+					# aP = - sum_N gamma * |Sf| / |df|
+					# aN = gamma * |Sf| / |df|
+					
+					lower[cmpt][faceIndex] = gammaInterpF * magSf[faceIndex]   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+							   
+					diag[cmpt][cellIndexP] -= gammaInterpF * magSf[faceIndex]  \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					diag[cmpt][cellIndexN] -= gammaInterpF * magSf[faceIndex]  \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					upper[cmpt][faceIndex] = gammaInterpF * magSf[faceIndex]   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+			
+			
+			# Add boundary contributions. The loop goes through the boundary
+			# patches and, depending on the boundaryType, defines the source and
+			# diagonal contributions by calling the appropriate functions
+			for boundaryPatch in mesh.connectivityData.boundary_:
+				boundaryName = boundaryPatch[0]
+				boundaryType = (psi.data.boundaryDefinition_[boundaryName])[0]
+				
+				boundaryContributionsFunctionString =						   \
+					"self.boundaryContributions.volScalarFieldGamma."		   \
+					+ boundaryType											   \
+					+ "(mesh, psi, boundaryPatch, source, diag, gamma)"
+				
+				source, diag = eval(boundaryContributionsFunctionString)
+					
+			return source, lower, diag, upper
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/laplacian_cy.pyx b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/laplacian_cy.pyx
new file mode 100644
index 0000000000000000000000000000000000000000..f16b38bde6b96470de013797a35d381da238cba4
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/laplacian_cy.pyx
@@ -0,0 +1,237 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the laplacian contributions for fvm, with Cython.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+cimport numpy as np
+
+from src.OpenFOAM.fields.volField.volScalarField.volScalarField import *
+from src.finiteVolume.fvMatrices.fvm.fvSchemes.laplacian.boundaryContributions import *
+
+cdef int noComponents
+cdef int nCells
+cdef int nInternalFaces
+cdef int cmpt
+cdef int faceIndex
+cdef int cellIndexP
+cdef int cellIndexN
+cdef float absPf
+cdef float absNf
+cdef float f
+cdef float gammaInterpF
+cdef int gammaCmpt
+
+class laplacianClass:
+	
+	class laplacian(laplacianBoundaryContributions):
+		
+		@classmethod
+		def linearOrthogonal(self, mesh, psi, fvVariables):
+			
+			gamma = fvVariables[0]
+			
+			# Check for type of diffusivity and call the appropriate function
+			if (type(gamma) == int or type(gamma) == float):
+				
+				source, lower, diag, upper = self.linearOrthogonalScalar(mesh, psi, fvVariables)
+				
+			elif (type(gamma) == volScalarField):
+				
+				source, lower, diag, upper = self.linearOrthogonalVolScalarField(mesh, psi, fvVariables)
+				
+			else:
+				
+				raise RuntimeError(											   \
+				"Laplacian with a diffusivity of type " + str(type(gamma)) +   \
+				" has not been implemented!")
+			
+			return source, lower, diag, upper
+			
+			
+		@classmethod
+		def linearOrthogonalScalar(self, mesh, psi, fvVariables):
+			
+			"""------------------- Cython declarations ----------------------"""
+			cdef float gamma
+			
+			cdef np.ndarray[np.float_t, ndim=1] magSf
+			cdef np.ndarray[np.float_t, ndim=2] C
+			cdef np.ndarray[np.float_t, ndim=2] source
+			cdef np.ndarray[np.float_t, ndim=2] lower
+			cdef np.ndarray[np.float_t, ndim=2] diag
+			cdef np.ndarray[np.float_t, ndim=2] upper
+			cdef np.ndarray[np.int_t, ndim=1] owner
+			cdef np.ndarray[np.int_t, ndim=1] neighbour
+			"""--------------------------------------------------------------"""
+			
+			gamma = fvVariables[0]
+			magSf = mesh.geometryData.magSf_
+			C = mesh.geometryData.C_
+			
+			noComponents = psi.noComponents_
+			
+			# Total number of cells
+			nCells = mesh.read("geometryData.meshSize")
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			source = np.zeros((noComponents, nCells), dtype = float)
+			
+			lower = np.zeros((noComponents, nInternalFaces), dtype = float)
+			diag = np.zeros((noComponents, nCells), dtype = float)
+			upper = np.zeros((noComponents, nInternalFaces), dtype = float)
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+					 
+			"""A MATRIX DEFINITION"""
+			# Add internal faces contributions
+			for cmpt in range(noComponents):
+				
+				for faceIndex in range(nInternalFaces):
+				
+					# aP = - sum_N gamma * |Sf| / |df|
+					# aN = gamma * |Sf| / |df|
+					
+					cellIndexP = owner[faceIndex]
+					cellIndexN = neighbour[faceIndex]
+					
+					lower[cmpt][faceIndex] = gamma * magSf[faceIndex] 		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+							   
+					diag[cmpt][cellIndexP] -= gamma * magSf[faceIndex]		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					diag[cmpt][cellIndexN] -= gamma * magSf[faceIndex]		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					upper[cmpt][faceIndex] = gamma * magSf[faceIndex]		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+			
+			
+			# Add boundary contributions. The loop goes through the boundary
+			# patches and, depending on the boundaryType, defines the source and
+			# diagonal contributions by calling the appropriate functions
+			for boundaryPatch in mesh.connectivityData.boundary_:
+				boundaryName = boundaryPatch[0]
+				boundaryType = (psi.data.boundaryDefinition_[boundaryName])[0]
+				
+				boundaryContributionsFunctionString =						   \
+					"self.boundaryContributions.scalarGamma."				   \
+					+ boundaryType											   \
+					+ "(mesh, psi, boundaryPatch, source, diag, gamma)"
+				
+				source, diag = eval(boundaryContributionsFunctionString)
+					
+			return source, lower, diag, upper
+			
+		@classmethod
+		def linearOrthogonalVolScalarField(self, mesh, psi, fvVariables):
+			
+			"""------------------- Cython declarations ----------------------"""
+			cdef np.ndarray[np.float_t, ndim=2] gammaValues
+			cdef np.ndarray[np.float_t, ndim=1] magSf
+			cdef np.ndarray[np.float_t, ndim=2] C
+			cdef np.ndarray[np.float_t, ndim=2] Cf
+			cdef np.ndarray[np.float_t, ndim=2] source
+			cdef np.ndarray[np.float_t, ndim=2] lower
+			cdef np.ndarray[np.float_t, ndim=2] diag
+			cdef np.ndarray[np.float_t, ndim=2] upper
+			cdef np.ndarray[np.int_t, ndim=1] owner
+			cdef np.ndarray[np.int_t, ndim=1] neighbour
+			"""--------------------------------------------------------------"""
+			
+			gamma = fvVariables[0]
+			gammaValues = gamma.data.cellValues_
+			
+			magSf = mesh.geometryData.magSf_
+			C = mesh.geometryData.C_
+			Cf = mesh.geometryData.Cf_
+			
+			noComponents = psi.noComponents_
+			
+			# Total number of cells
+			nCells = mesh.read("geometryData.meshSize")
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			source = np.zeros((noComponents, nCells), dtype = float)
+			
+			lower = np.zeros((noComponents, nInternalFaces), dtype = float)
+			diag = np.zeros((noComponents, nCells), dtype = float)
+			upper = np.zeros((noComponents, nInternalFaces), dtype = float)
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+			
+			# Gamma is a volScalarField, so only one component
+			gammaCmpt = 0
+			
+			"""A MATRIX DEFINITION"""
+			# Add internal faces contributions
+			for cmpt in range(noComponents):
+				
+				for faceIndex in range(nInternalFaces):
+					
+					cellIndexP = owner[faceIndex]
+					cellIndexN = neighbour[faceIndex]
+					
+					absPf = np.linalg.norm(Cf[faceIndex] - C[cellIndexP])
+					absNf = np.linalg.norm(Cf[faceIndex] - C[cellIndexN])
+					
+					f = absNf / (absPf + absNf)
+
+					gammaInterpF = 											   \
+						f * gammaValues[gammaCmpt][cellIndexP] + 			   \
+						(1 - f) * gammaValues[gammaCmpt][cellIndexN]
+				
+					# aP = - sum_N gamma * |Sf| / |df|
+					# aN = gamma * |Sf| / |df|
+					
+					lower[cmpt][faceIndex] = gammaInterpF * magSf[faceIndex]   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+							   
+					diag[cmpt][cellIndexP] -= gammaInterpF * magSf[faceIndex]  \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					diag[cmpt][cellIndexN] -= gammaInterpF * magSf[faceIndex]  \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					upper[cmpt][faceIndex] = gammaInterpF * magSf[faceIndex]   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+			
+			
+			# Add boundary contributions. The loop goes through the boundary
+			# patches and, depending on the boundaryType, defines the source and
+			# diagonal contributions by calling the appropriate functions
+			for boundaryPatch in mesh.connectivityData.boundary_:
+				boundaryName = boundaryPatch[0]
+				boundaryType = (psi.data.boundaryDefinition_[boundaryName])[0]
+				
+				boundaryContributionsFunctionString =						   \
+					"self.boundaryContributions.volScalarFieldGamma."		   \
+					+ boundaryType											   \
+					+ "(mesh, psi, boundaryPatch, source, diag, gamma)"
+				
+				source, diag = eval(boundaryContributionsFunctionString)
+					
+			return source, lower, diag, upper
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/laplacian_py.py b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/laplacian_py.py
new file mode 100644
index 0000000000000000000000000000000000000000..5007916f8b758627e9baa24984679a6227b3a9ed
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/laplacian_py.py
@@ -0,0 +1,197 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the laplacian contributions for fvm, pure Python.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+from src.OpenFOAM.fields.volField.volScalarField.volScalarField import *
+from src.finiteVolume.fvMatrices.fvm.fvSchemes.laplacian.boundaryContributions import *
+
+class laplacianClass:
+	
+	class laplacian(laplacianBoundaryContributions):
+		
+		@classmethod
+		def linearOrthogonal(self, mesh, psi, fvVariables):
+			
+			gamma = fvVariables[0]
+			
+			# Check for type of diffusivity and call the appropriate function
+			if (type(gamma) == int or type(gamma) == float):
+				
+				source, lower, diag, upper = self.linearOrthogonalScalar(mesh, psi, fvVariables)
+				
+			elif (type(gamma) == volScalarField):
+				
+				source, lower, diag, upper = self.linearOrthogonalVolScalarField(mesh, psi, fvVariables)
+				
+			else:
+				
+				raise RuntimeError(											   \
+				"Laplacian with a diffusivity of type " + str(type(gamma)) +   \
+				" has not been implemented!")
+			
+			return source, lower, diag, upper
+			
+			
+		@classmethod
+		def linearOrthogonalScalar(self, mesh, psi, fvVariables):
+			
+			gamma = fvVariables[0]
+			magSf = mesh.geometryData.magSf_
+			C = mesh.geometryData.C_
+			
+			noComponents = psi.noComponents_
+			
+			# Total number of cells
+			nCells = mesh.read("geometryData.meshSize")
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			source = np.zeros((noComponents, nCells), dtype = float)
+			
+			lower = np.zeros((noComponents, nInternalFaces), dtype = float)
+			diag = np.zeros((noComponents, nCells), dtype = float)
+			upper = np.zeros((noComponents, nInternalFaces), dtype = float)
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+					 
+			"""A MATRIX DEFINITION"""
+			# Add internal faces contributions
+			for cmpt in range(noComponents):
+				
+				for faceIndex in range(nInternalFaces):
+				
+					# aP = - sum_N gamma * |Sf| / |df|
+					# aN = gamma * |Sf| / |df|
+					
+					cellIndexP = owner[faceIndex]
+					cellIndexN = neighbour[faceIndex]
+					
+					lower[cmpt][faceIndex] = gamma * magSf[faceIndex] 		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+							   
+					diag[cmpt][cellIndexP] -= gamma * magSf[faceIndex]		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					diag[cmpt][cellIndexN] -= gamma * magSf[faceIndex]		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					upper[cmpt][faceIndex] = gamma * magSf[faceIndex]		   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+			
+			
+			# Add boundary contributions. The loop goes through the boundary
+			# patches and, depending on the boundaryType, defines the source and
+			# diagonal contributions by calling the appropriate functions
+			for boundaryPatch in mesh.connectivityData.boundary_:
+				boundaryName = boundaryPatch[0]
+				boundaryType = (psi.data.boundaryDefinition_[boundaryName])[0]
+				
+				boundaryContributionsFunctionString =						   \
+					"self.boundaryContributions.scalarGamma."				   \
+					+ boundaryType											   \
+					+ "(mesh, psi, boundaryPatch, source, diag, gamma)"
+				
+				source, diag = eval(boundaryContributionsFunctionString)
+					
+			return source, lower, diag, upper
+			
+		@classmethod
+		def linearOrthogonalVolScalarField(self, mesh, psi, fvVariables):
+			
+			gamma = fvVariables[0]
+			gammaValues = gamma.data.cellValues_
+			
+			magSf = mesh.geometryData.magSf_
+			C = mesh.geometryData.C_
+			Cf = mesh.geometryData.Cf_
+			
+			noComponents = psi.noComponents_
+			
+			# Total number of cells
+			nCells = mesh.read("geometryData.meshSize")
+			# Number of internal faces
+			nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+			
+			source = np.zeros((noComponents, nCells), dtype = float)
+			
+			lower = np.zeros((noComponents, nInternalFaces), dtype = float)
+			diag = np.zeros((noComponents, nCells), dtype = float)
+			upper = np.zeros((noComponents, nInternalFaces), dtype = float)
+			
+			owner = mesh.connectivityData.owner_
+			neighbour = mesh.connectivityData.neighbour_
+			
+			# Gamma is a volScalarField, so only one component
+			gammaCmpt = 0
+			
+			"""A MATRIX DEFINITION"""
+			# Add internal faces contributions
+			for cmpt in range(noComponents):
+				
+				for faceIndex in range(nInternalFaces):
+					
+					cellIndexP = owner[faceIndex]
+					cellIndexN = neighbour[faceIndex]
+					
+					absPf = np.linalg.norm(Cf[faceIndex] - C[cellIndexP])
+					absNf = np.linalg.norm(Cf[faceIndex] - C[cellIndexN])
+					
+					f = absNf / (absPf + absNf)
+
+					gammaInterpF = 											   \
+						f * gammaValues[gammaCmpt][cellIndexP] + 			   \
+						(1 - f) * gammaValues[gammaCmpt][cellIndexN]
+				
+					# aP = - sum_N gamma * |Sf| / |df|
+					# aN = gamma * |Sf| / |df|
+					
+					lower[cmpt][faceIndex] = gammaInterpF * magSf[faceIndex]   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+							   
+					diag[cmpt][cellIndexP] -= gammaInterpF * magSf[faceIndex]  \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					diag[cmpt][cellIndexN] -= gammaInterpF * magSf[faceIndex]  \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+										
+					upper[cmpt][faceIndex] = gammaInterpF * magSf[faceIndex]   \
+								/ (np.linalg.norm(C[cellIndexN] - C[cellIndexP]))
+			
+			
+			# Add boundary contributions. The loop goes through the boundary
+			# patches and, depending on the boundaryType, defines the source and
+			# diagonal contributions by calling the appropriate functions
+			for boundaryPatch in mesh.connectivityData.boundary_:
+				boundaryName = boundaryPatch[0]
+				boundaryType = (psi.data.boundaryDefinition_[boundaryName])[0]
+				
+				boundaryContributionsFunctionString =						   \
+					"self.boundaryContributions.volScalarFieldGamma."		   \
+					+ boundaryType											   \
+					+ "(mesh, psi, boundaryPatch, source, diag, gamma)"
+				
+				source, diag = eval(boundaryContributionsFunctionString)
+					
+			return source, lower, diag, upper
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/setup_boundaryContributions.py b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/setup_boundaryContributions.py
new file mode 100644
index 0000000000000000000000000000000000000000..8fb1852aff8d7802522a61bcfcc8eedfbaecd6fa
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/setup_boundaryContributions.py
@@ -0,0 +1,31 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Setup file for Cythonizing fvm laplacian boundary contributions
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import os
+
+from distutils.core import setup
+from Cython.Build import cythonize
+
+directives = {'language_level': 3}
+
+currDir = os.path.dirname(os.path.abspath(__file__))
+setup(ext_modules = cythonize(currDir + '/boundaryContributions.pyx', compiler_directives=directives))
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/setup_laplacian.py b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/setup_laplacian.py
new file mode 100644
index 0000000000000000000000000000000000000000..593eaee0e997a6e17f2730ce80f2d856fd2afa1d
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvSchemes/laplacian/setup_laplacian.py
@@ -0,0 +1,31 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Setup file for Cythonizing fvm of the laplacian term
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import os
+
+from distutils.core import setup
+from Cython.Build import cythonize
+
+directives = {'language_level': 3}
+
+currDir = os.path.dirname(os.path.abspath(__file__))
+setup(ext_modules = cythonize(currDir + '/laplacian.pyx', compiler_directives=directives))
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/fvm/fvm.py b/foamPython/src/finiteVolume/fvMatrices/fvm/fvm.py
new file mode 100644
index 0000000000000000000000000000000000000000..e13bcbff5755734f26207ae384a2fad126c1c7f3
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/fvm/fvm.py
@@ -0,0 +1,50 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Finite volume matrix contributions, implicit
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.finiteVolume.fvMatrices.fvm.fvSchemes.fvSchemes import *
+from src.finiteVolume.fvMatrices.fvMatrix import *
+
+class fvm(fvMatrix, fvSchemes):
+
+	"""------------- Matrix components definition functions ----------------"""
+	
+	@classmethod
+	def defineMatrix(self, psi, operator, scheme, fvVariables):
+		
+		mesh = psi.data.mesh_
+		
+		# The dictionary of operator classes
+		operatorsDict = self.operatorsDict_
+		
+		# Assign the appropriate operator class
+		try:
+			operatorClass = operatorsDict[operator]
+		except:
+			raise RuntimeError("Operator " + operator + " is not implemented!")
+		
+		# Assign the appropriate function inside the operator class
+		operatorSchemeFunc = eval("operatorClass." + scheme)
+		
+		# Call the appropriate function
+		source, lower, diag, upper = operatorSchemeFunc(mesh, psi, fvVariables)
+		
+		return source, lower, diag, upper
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMatrices/include.py b/foamPython/src/finiteVolume/fvMatrices/include.py
new file mode 100644
index 0000000000000000000000000000000000000000..02efd8c2be965640b93385b7d202c464ad5bb080
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMatrices/include.py
@@ -0,0 +1,24 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Include file for fvMatrix.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.finiteVolume.fvMatrices.fvm.fvm import *
+from src.finiteVolume.fvMatrices.fvc.fvc import *
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvMesh/fvMesh.py b/foamPython/src/finiteVolume/fvMesh/fvMesh.py
new file mode 100644
index 0000000000000000000000000000000000000000..7294b60b16741ca4178a884cac3f67206323c108
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvMesh/fvMesh.py
@@ -0,0 +1,567 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Mesh data needed to do the Finite Volume discretisation.
+    
+    Note: Cell and face centres are calculated using averages of vertices.
+    Note: The mesh can be initialized using the functions present here, or
+    by using the pre-existing Ofpp package. This is modifyed by
+    commenting/uncommenting the appropriate lines in the
+    constructFromPolyMeshFolder function, and the import statement at the
+    begining of this file
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+import subprocess
+import re
+# import Ofpp
+
+class fvMesh:
+	
+	class fvMeshGeometryData:
+		def __init__(self):
+			V_ 			= None # Cell volumes, volume scalar field
+			Sf_ 		= None # Face area vectors, surface vector field
+			magSf_ 		= None # Mag face area vectors, surface scalar field
+			C_ 			= None # Cell centres, volume vector field
+			Cf_ 		= None # Face centres, surface vector field
+			meshSize_	= None # Number of cells in the mesh
+		
+	class fvMeshConnectivityData:
+		def __init__(self):
+			points_ 	= None # Points list
+			faces_		= None # Faces list
+			owner_		= None # Owners list
+			neighbour_	= None # Neighbours list
+			boundary_	= None # Boundaries list
+	
+	"""------------------------- Constructors -------------------------------"""		
+	# Main constructor
+	def __init__(self, volumes, surfaces, cellCentres, faceCentres, points,    \
+				 faces, owner, neighbour, boundary):
+		
+		self.geometryData = self.fvMeshGeometryData()
+		self.connectivityData = self.fvMeshConnectivityData()
+		
+		# Geometry data
+		self.geometryData.V_ 			= volumes
+		self.geometryData.Sf_ 			= surfaces
+		self.calculateMagSf()
+		self.geometryData.C_ 			= cellCentres
+		self.geometryData.Cf_ 			= faceCentres
+		self.geometryData.meshSize_ 	= self.geometryData.V_.size
+	
+		# Connectivity data
+		self.connectivityData.points_	= points
+		self.connectivityData.faces_	= faces
+		self.connectivityData.owner_	= owner
+		self.connectivityData.neighbour_= neighbour
+		self.connectivityData.boundary_	= boundary
+		
+		print("Mesh created.\n")
+	
+	# Constructor which reads in OpenFOAM-format polyMesh folder and calculates
+	# the necessary metrics from that
+	@classmethod
+	def constructFromPolyMeshFolder(cls, polyMeshLocation):
+		
+		print("Creating mesh.\n")
+		
+		# """ --------- Ofpp method --------- """
+		# # Reference: https://github.com/xu-xianghua/ofpp
+		# mesh = Ofpp.FoamMesh(".")
+		# points 			= mesh.points
+		# faces 			= np.array(mesh.faces, dtype=object)
+		# owner			= np.array(mesh.owner)
+		# neighbour		= np.array(mesh.neighbour[:mesh.num_inner_face])
+			
+		# boundaryArray = np.empty(0, dtype = set)
+		
+		# nBoundary = 0
+		# for i in mesh.boundary:
+			# boundaryName = i.decode('ASCII')
+			# boundaryType = (mesh.boundary[i])[0].decode('ASCII')
+			# nFaces = (mesh.boundary[i])[1]
+			# startFace = (mesh.boundary[i])[2]
+			# boundaryArray = np.append(boundaryArray, None)
+			# boundaryArray[nBoundary] =										   \
+				# [boundaryName, boundaryType, nFaces, startFace]
+				
+			# nBoundary += 1
+		
+		# boundary = boundaryArray
+		# """ --------- ----------- --------- """	
+
+		""" --------- Custom method --------- """
+		# Connectivity data
+		points			= cls.readPointsFromPolyMesh(polyMeshLocation)
+		faces 			= cls.readFacesFromPolyMesh(polyMeshLocation)
+		owner			= cls.readOwnerFromPolyMesh(polyMeshLocation)
+		neighbour		= cls.readNeighbourFromPolyMesh(polyMeshLocation)
+		boundary 		= cls.readBoundaryFromPolyMesh(polyMeshLocation)
+		""" --------- ----------- --------- """
+		
+		# Geometry data
+		faceCentres 	= cls.calculateFaceCentres(points, faces)
+		surfaces 		= cls.calculateFaceAreaVectors(points, faces,		   \
+						  faceCentres)
+		cellCentres 	= cls.calculateCellCentres(faceCentres, owner, 		   \
+						  neighbour)
+		volumes 		= cls.calculateCellVolumes(owner, neighbour, surfaces, \
+						  faceCentres, cellCentres)
+		
+		return cls(volumes, surfaces, cellCentres, faceCentres, points, faces, \
+				   owner, neighbour, boundary)
+	
+	"""---------------------- polyMesh reading functions --------------------"""
+	
+	@classmethod
+	def readPointsFromPolyMesh(cls, polyMeshLocation):
+		
+		# Read in file
+		file = open(polyMeshLocation + "/points").read()
+		# Remove comments
+		file = cls.removeCppComments(file)
+		# Remove FoamFile dictionary
+		file = cls.removeFoamFile(file)
+		# Split the file into tokens
+		file = file.split()
+		
+		nPoints = int(file[0])
+		
+		# Initialise the end result: array of points
+		pointsArray = np.empty((nPoints, 3), dtype = float)
+		
+		# Variable used to know in which point the file is
+		pointCounter = 0
+		# Variable used to determine the X, Y or Z coordinate
+		componentCounter = 0
+		for i in file[2:-1]:
+			coordinate = (i.replace('(', '')).replace(')', '')
+			
+			pointsArray[pointCounter][componentCounter] = coordinate
+			
+			if (componentCounter == 2):
+				componentCounter = 0
+				pointCounter += 1
+			else:
+				componentCounter += 1
+				
+		return pointsArray
+
+	
+	@classmethod
+	def readFacesFromPolyMesh(cls, polyMeshLocation):
+		
+		# Read in file
+		file = open(polyMeshLocation + "/faces").read()
+		# Remove comments
+		file = cls.removeCppComments(file)
+		# Remove FoamFile dictionary
+		file = cls.removeFoamFile(file)
+		# Split the file into tokens
+		file = file.split()
+		
+		nFaces = int(file[0])
+
+		facesArray = np.empty(nFaces, dtype = object)
+		
+		# Variable used to know in which face the file is
+		faceCounter = 0
+		# Variable used to know which is the number of the point in the face
+		pointInFaceCounter = 0
+		for i in file[2:-1]:
+			
+			if "(" in i:
+				splitI = i.split("(")
+				# Find out the size of the new face
+				newFaceSize = int(splitI[0])
+				# Initialize the new face
+				newFace = np.empty(newFaceSize, dtype = int)
+				# Insert the first point of the new face
+				newFace[0] = int(splitI[1])
+				pointInFaceCounter = 0
+			elif ")" in i:
+				pointInFaceCounter += 1
+				newFace[pointInFaceCounter] = int(i.replace(")", ""))
+				facesArray[faceCounter] = newFace
+				faceCounter += 1
+			else:
+				pointInFaceCounter += 1
+				newFace[pointInFaceCounter] = int(i)
+
+		return facesArray
+	
+	@classmethod
+	def readOwnerFromPolyMesh(cls, polyMeshLocation):
+		
+		# Read in file
+		file = open(polyMeshLocation + "/owner").read()
+		# Remove comments
+		file = cls.removeCppComments(file)
+		# Remove FoamFile dictionary
+		file = cls.removeFoamFile(file)
+		# Split the file into tokens
+		file = file.split()
+		
+		nOwners = int(file[0])
+		
+		ownerArray = np.empty(nOwners, dtype = int)
+		
+		ownerCounter = 0
+		for i in file[2:-1]:
+			ownerArray[ownerCounter] = i
+			ownerCounter += 1
+		
+		return ownerArray
+	
+	@classmethod
+	def readNeighbourFromPolyMesh(cls, polyMeshLocation):
+		
+		# Read in file
+		file = open(polyMeshLocation + "/neighbour").read()
+		# Remove comments
+		file = cls.removeCppComments(file)
+		# Remove FoamFile dictionary
+		file = cls.removeFoamFile(file)
+		# Split the file into tokens
+		file = file.split()
+		
+		nNeighbours = int(file[0])
+		
+		neighbourArray = np.empty(nNeighbours, dtype = int)
+		
+		neighbourCounter = 0
+		for i in file[2:-1]:
+			neighbourArray[neighbourCounter] = i
+			neighbourCounter += 1
+		
+		return neighbourArray
+	
+	@classmethod
+	def readBoundaryFromPolyMesh(cls, polyMeshLocation):
+		
+		# Read in file
+		file = open(polyMeshLocation + "/boundary").read()
+		# Remove comments
+		file = cls.removeCppComments(file)
+		# Remove FoamFile dictionary
+		file = cls.removeFoamFile(file)
+		# Split the file into tokens
+		file = file.split()
+		
+		nBoundary = int(file[0])
+
+		# Initialise the end result: array of boundaries
+		boundaryArray = np.empty(nBoundary, dtype = set)
+
+		# Loop to put in place holder values into the final array
+		for i in range(nBoundary):
+			boundaryArray[i] = ["boundaryName", "boundaryType", 0, 0]
+
+		# Variables/flags used for going through the boundary file
+		boundaryCounter = 0
+		lookingForName = True
+		
+		lookingForType = False
+		lookingFornFaces = False
+		foundnFaces = False
+		lookingForstartFace = False
+		foundstartFace = False
+		assembledBoundary = False
+		for i in file[2:-1]:
+			
+			if (i == "}") or (i == "{"):
+				continue
+			elif(lookingForName):
+				boundaryName = i
+				lookingForName = False
+				lookingForType = True
+			elif (lookingForType):
+				if (i == 'type'):
+					continue
+				else:
+					boundaryType = i.replace(";", "")
+					lookingForType = False
+					lookingFornFaces = True
+			elif (lookingFornFaces):
+				if (i == 'nFaces'):
+					foundnFaces = True
+				elif (not foundnFaces):
+					continue
+				else:
+					nFaces = i.replace(";", "")
+					foundnFaces = False
+					lookingFornFaces = False
+					lookingForstartFace = True
+			elif (lookingForstartFace):
+				if (i == 'startFace'):
+					foundstartFace = True
+				elif (not foundstartFace):
+					continue
+				else:
+					startFace = i.replace(";", "")
+					foundstartFace = False
+					lookingForstartFace = False
+					lookingForName = True
+					
+					assembledBoundary = True
+			
+			if (assembledBoundary):
+				boundaryArray[boundaryCounter][0] = boundaryName
+				boundaryArray[boundaryCounter][1] = boundaryType
+				boundaryArray[boundaryCounter][2] = int(nFaces)
+				boundaryArray[boundaryCounter][3] = int(startFace)
+				
+				boundaryCounter += 1
+				assembledBoundary = False
+		
+		return boundaryArray
+		
+	def removeCppComments(text):
+		def replacer(match):
+			s = match.group(0)
+			if s.startswith('/'):
+				return " " # note: a space and not an empty string
+			else:
+				return s
+		pattern = re.compile(
+			r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',		   \
+			re.DOTALL | re.MULTILINE
+		)
+		
+		return re.sub(pattern, replacer, text)
+		
+	def removeFoamFile(file):
+		inFoamFile = False
+		lineCounter = 0
+		for i in file:
+			lineCounter += 1
+			if i == '}':
+				break
+
+		return file[lineCounter:]
+		
+					
+	"""------------------------- Geometric functions ------------------------"""
+	
+	# Function to calculate the face area magnitudes using the face area vectors
+	def calculateMagSf(self):
+		
+		# Define the size of the result array
+		self.geometryData.magSf_ = np.empty(np.size(self.geometryData.Sf_,	   \
+								   axis = 0))
+		
+		# The result is the magnitude of face area vectors
+		for i in range(self.geometryData.magSf_.size): # linalg.norm does
+													   # not seem to
+													   # vectorise
+			self.geometryData.magSf_[i] = np.linalg.norm( 					   \
+										  self.geometryData.Sf_[i])
+		
+	def calculateFaceAreaVectors(pointList, faceList, faceCentresList):
+		
+		result = np.zeros((np.size(faceList, axis = 0), 3))
+		
+		# For all faces
+		for faceIndex in range(np.size(result, axis = 0)):
+			
+			# The centre of the face
+			faceCentre = faceCentresList[faceIndex]
+			
+			# For all points in the face
+			for j in range(np.size(faceList[faceIndex])):
+				
+				# First point:
+				p1 = pointList[(faceList[faceIndex])[j]]
+				
+				# Second point:
+				# If not at the last point in the face
+				if (j < np.size(faceList[faceIndex]) - 1):
+					
+					p2 = pointList[(faceList[faceIndex])[j+1]]
+				
+				# If at the last point of the face, the next point is the first
+				# (0-th)
+				else:
+					p2 = pointList[(faceList[faceIndex])[0]]
+				
+				# The vectors which define the triangle
+				vec1 = p2 - p1
+				vec2 = faceCentre - p1
+				
+				# Face area vector of the triangle between the current and
+				# next points and the face centre
+				result[faceIndex] += 0.5 * np.cross(vec1, vec2)
+		
+		return result
+	
+	def calculateFaceCentres(pointList, faceList):
+		# Average of coordinates of vertices
+		
+		result = np.zeros((np.size(faceList, axis = 0), 3))
+		
+		# For all faces
+		for i in range(np.size(faceList, axis = 0)):
+			nPointsInFace = 0
+			
+			# For every point in face
+			for j in faceList[i]:
+				
+				# Add up the coordinates of points
+				result[i] += pointList[j]
+				
+				# Counter of numbers of points in face
+				nPointsInFace +=1
+			
+			# Divide sums of coordinates by number of points in face
+			result[i] /= nPointsInFace
+			
+		return result
+		
+	def calculateCellCentres(faceCentresList, ownerList, neighbourList):
+		# Average of coordinates of face centres
+		
+		result = np.zeros((ownerList.max() + 1, 3))
+		
+		# Counter of number of faces in cell
+		nFacesInCells = np.zeros(ownerList.max() + 1)
+		
+		# For all owners
+		for i in range(ownerList.size):
+			result[ownerList[i]] += faceCentresList[i]
+			nFacesInCells[ownerList[i]] += 1
+		
+		# For all neighbours
+		for i in range(neighbourList.size):
+			result[neighbourList[i]] += faceCentresList[i]
+			nFacesInCells[neighbourList[i]] += 1
+		
+		# Divide sums of coordinates by number of faces in cells
+		for i in range(nFacesInCells.size):
+			result[i] /= nFacesInCells[i]
+		
+		return result
+		
+	def calculateCellVolumes(ownerList, neighbourList, surfaces, faceCentres,  \
+							 cellCentres):
+		# V = sum_faces{1 / 3 * (faceCentre - cellCentre) * faceAreaVector}
+		
+		result = np.zeros(ownerList.max() + 1)
+		
+		facesInCellsList = np.empty(ownerList.max() + 1, dtype=object)
+		
+		# Go through owners and add faces to cells
+		for i in range(ownerList.size):
+			facesInCellsList[ownerList[i]] = np.append(facesInCellsList[ 	   \
+			                                           ownerList[i]], [i])
+			
+		# Go through neighbours and add faces to cells
+		for i in range(neighbourList.size):
+			facesInCellsList[neighbourList[i]] = np.append(facesInCellsList[   \
+												 neighbourList[i]], [i])
+			
+		# Delete the 0-th element in the arrays (all cell face lists start with
+		# "None" without this)
+		for i in range(np.size(facesInCellsList, axis = 0)):
+			facesInCellsList[i] = np.delete(facesInCellsList[i], 0)
+		
+		# Final result calculation
+		for i in range(result.size):
+			for j in facesInCellsList[i]:
+				result[i] += np.linalg.norm((faceCentres[j] - cellCentres[i])  \
+							 * surfaces[j])
+			result[i] = result[i] / 3
+		
+		return result
+
+	
+	"""------------------------- Access functions ---------------------------"""
+	
+	# Generic data access function. Input is a string which is the name of the
+	# data we are interested in, et.g. 'geometryData.V',
+	# 'connectivityData.owner', etc. Additionaly, the second argument can also
+	# be the index of the point/face/cell, etc. we are interested in
+	def read(self, inputString, i = None):
+		
+		readListString = "self." + inputString + "_"
+		
+		if (i == None):
+			return eval(readListString)
+		else:
+			try:
+				return eval(readListString + "[" + str(i) + "]")
+			except RuntimeError:
+				print("The requested index is out of bounds!")
+				
+	# Function which prints all of the data present in the mesh
+	def printAll(self):		
+		
+		print("")
+		print("List of volumes:")
+		print(self.read("geometryData.V"))
+		print("")
+		print("List of face area vectors:")
+		print(self.read("geometryData.Sf"))
+		print("")
+		print("List of face area vector magnitudes:")
+		print(self.read("geometryData.magSf"))
+		print("")
+		print("List of cell centres:")
+		print(self.read("geometryData.C"))
+		print("")
+		print("List of face centres:")
+		print(self.read("geometryData.Cf"))
+		print("")
+		print("List of points:")
+		print(self.read("connectivityData.points"))
+		print("")
+		print("List of faces:")
+		print(self.read("connectivityData.faces"))
+		print("")
+		print("List of owners:")
+		print(self.read("connectivityData.owner"))
+		print("")
+		print("List of neighbours:")
+		print(self.read("connectivityData.neighbour"))
+		print("")
+		print("List of boundaries:")
+		print(self.read("connectivityData.boundary"))
+		print("")
+		
+		print("Number of volumes: " + 										   \
+			  str(np.size(self.read("geometryData.V"), axis = 0)))
+		print("Number of face area vectors: " + 							   \
+			  str(np.size(self.read("geometryData.Sf"), axis = 0)))
+		print("Number of face area vector magnitudes: " + 					   \
+			  str(np.size(self.read("geometryData.magSf"), axis = 0)))
+		print("Number of cell centres: " + 									   \
+			  str(np.size(self.read("geometryData.C"), axis = 0)))
+		print("Number of faceCentres: " + 									   \
+			  str(np.size(self.read("geometryData.Cf"), axis = 0)))
+		print("Number of points: " + 										   \
+			  str(np.size(self.read("connectivityData.points"), axis = 0)))
+		print("Number of faces: " + 										   \
+			  str(np.size(self.read("connectivityData.faces"), axis = 0)))
+		print("Number of owners: " + 										   \
+			  str(np.size(self.read("connectivityData.owner"))))
+		print("Number of neighbours: " + 									   \
+			  str(np.size(self.read("connectivityData.neighbour"))))
+		print("Number of boundaries: " + 									   \
+			  str(np.size(self.read("connectivityData.boundary"), axis = 0)))
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvSolution/fvSolution.py b/foamPython/src/finiteVolume/fvSolution/fvSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..635d5fd7a9a692fe587e5a9dd598a0a995f397f9
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvSolution/fvSolution.py
@@ -0,0 +1,207 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the functions needed to solve the fvMatrix, pure Python
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+class fvSolution():
+	
+	# Default solution parameters
+	minIterDefault_ 	= 0
+	maxIterDefault_ 	= 1000
+	toleranceDefault_	= 0
+	relTolDefault_		= 0
+	
+	"""----------------------- Other functions ------------------------------"""		
+	@classmethod
+	def calculateResidual(self, fvMatrix, cmpt, normFactor):
+			
+		mesh = fvMatrix.data.mesh_
+		
+		psi = fvMatrix.data.psi_.data.cellValues_[cmpt]
+		
+		# Total number of cells
+		nCells =  mesh.geometryData.meshSize_
+		# Number of internal faces
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+
+		source = fvMatrix.data.source_[cmpt]
+		
+		lower = fvMatrix.data.lower_[cmpt]
+		diag = fvMatrix.data.diag_[cmpt]
+		upper = fvMatrix.data.upper_[cmpt]
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		# Calculate Apsi
+		Apsi = self.Amul(fvMatrix, cmpt)
+		
+		# Calculate normalisation factor for the first iteration
+		if (normFactor == None):
+			normFactor = self.normFactor(Apsi, fvMatrix, cmpt)
+			
+		# Initial residual guess
+		resArray = np.absolute(source - Apsi)
+		
+		# Geometric sum of residual guess magnitudes in all cells
+		resSum = self.gSumMag(resArray, mesh.geometryData.V_)
+		
+		return resSum/normFactor, normFactor
+	
+	
+	def checkConvergence(residual, resInit, tolerance, relTol):
+		
+		if (residual > tolerance and residual/resInit > relTol):
+			
+			return False
+		
+		else:
+			
+			return True
+	
+	
+	def Amul(fvMatrix, cmpt):
+		
+		mesh = fvMatrix.data.mesh_
+		
+		psi = fvMatrix.data.psi_.data.cellValues_[cmpt]
+		
+		nCells =  mesh.geometryData.meshSize_
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		
+		source = fvMatrix.data.source_[cmpt]
+		lower = fvMatrix.data.lower_[cmpt]
+		diag = fvMatrix.data.diag_[cmpt]
+		upper = fvMatrix.data.upper_[cmpt]
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		resultArray = np.empty(nCells, dtype = float)
+		
+		resultArray = diag * psi
+		
+		for faceIndex in range(nInternalFaces):
+			
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			resultArray[ownIndex] += upper[faceIndex] * psi[neiIndex]
+			resultArray[neiIndex] += lower[faceIndex] * psi[ownIndex]
+			
+		return resultArray
+		
+	
+	@classmethod
+	def normFactor(self, Apsi, fvMatrix, cmpt):
+		
+		psi = fvMatrix.data.psi_
+		
+		mesh = fvMatrix.data.mesh_
+		
+		source = fvMatrix.data.source_
+		
+		
+		sumA = self.sumA(fvMatrix, cmpt)
+		
+		sumA *= self.gAverage(psi, cmpt)
+		
+		return (self.gSum													   \
+				   (														   \
+						(np.absolute(Apsi - sumA) + np.absolute(source[cmpt] - sumA)),\
+						mesh.geometryData.V_								   \
+				   ) + 1.0e-15 )
+	
+	
+	def sumA(fvMatrix, cmpt):
+		
+		mesh = fvMatrix.data.mesh_
+		
+		nCells =  mesh.geometryData.meshSize_
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+
+		lower = fvMatrix.data.lower_
+		diag = fvMatrix.data.diag_
+		upper = fvMatrix.data.upper_
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		resultArray = np.empty(nCells, dtype = float)
+		
+		# Vectorised
+		resultArray = np.copy(diag[cmpt])
+		
+		for faceIndex in range(nInternalFaces):
+			
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			resultArray[ownIndex] += upper[cmpt][faceIndex]
+			resultArray[neiIndex] += lower[cmpt][faceIndex]
+		
+		return resultArray
+	
+	
+	def gAverage(psi, cmpt):
+		
+		mesh = psi.data.mesh_
+		
+		psiValues = psi.data.cellValues_
+		
+		V = mesh.geometryData.V_
+		
+		nCells =  mesh.geometryData.meshSize_
+		
+		averageV = np.average(V)
+		
+		result = np.empty(nCells, dtype = float)
+		
+		# Vectorised
+		result = psiValues[cmpt] * V / averageV
+		
+		psiAverage = np.average(result)
+		
+		return psiAverage
+		
+	
+	def gSum(field, scalingField):
+		
+		nCells = np.size(field)
+		
+		scalingFieldAverage = np.average(scalingField)
+		
+		sum = np.sum(field * scalingField / scalingFieldAverage)
+		
+		return sum
+	
+	
+	def gSumMag(field, scalingField):
+		
+		nCells = np.size(field)
+		
+		scalingFieldAverage = np.average(scalingField)
+		
+		sum = np.sum(np.absolute(field * scalingField) / scalingFieldAverage)
+		
+		return sum
+		
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvSolution/fvSolution.py.orig b/foamPython/src/finiteVolume/fvSolution/fvSolution.py.orig
new file mode 100644
index 0000000000000000000000000000000000000000..635d5fd7a9a692fe587e5a9dd598a0a995f397f9
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvSolution/fvSolution.py.orig
@@ -0,0 +1,207 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the functions needed to solve the fvMatrix, pure Python
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+class fvSolution():
+	
+	# Default solution parameters
+	minIterDefault_ 	= 0
+	maxIterDefault_ 	= 1000
+	toleranceDefault_	= 0
+	relTolDefault_		= 0
+	
+	"""----------------------- Other functions ------------------------------"""		
+	@classmethod
+	def calculateResidual(self, fvMatrix, cmpt, normFactor):
+			
+		mesh = fvMatrix.data.mesh_
+		
+		psi = fvMatrix.data.psi_.data.cellValues_[cmpt]
+		
+		# Total number of cells
+		nCells =  mesh.geometryData.meshSize_
+		# Number of internal faces
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+
+		source = fvMatrix.data.source_[cmpt]
+		
+		lower = fvMatrix.data.lower_[cmpt]
+		diag = fvMatrix.data.diag_[cmpt]
+		upper = fvMatrix.data.upper_[cmpt]
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		# Calculate Apsi
+		Apsi = self.Amul(fvMatrix, cmpt)
+		
+		# Calculate normalisation factor for the first iteration
+		if (normFactor == None):
+			normFactor = self.normFactor(Apsi, fvMatrix, cmpt)
+			
+		# Initial residual guess
+		resArray = np.absolute(source - Apsi)
+		
+		# Geometric sum of residual guess magnitudes in all cells
+		resSum = self.gSumMag(resArray, mesh.geometryData.V_)
+		
+		return resSum/normFactor, normFactor
+	
+	
+	def checkConvergence(residual, resInit, tolerance, relTol):
+		
+		if (residual > tolerance and residual/resInit > relTol):
+			
+			return False
+		
+		else:
+			
+			return True
+	
+	
+	def Amul(fvMatrix, cmpt):
+		
+		mesh = fvMatrix.data.mesh_
+		
+		psi = fvMatrix.data.psi_.data.cellValues_[cmpt]
+		
+		nCells =  mesh.geometryData.meshSize_
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		
+		source = fvMatrix.data.source_[cmpt]
+		lower = fvMatrix.data.lower_[cmpt]
+		diag = fvMatrix.data.diag_[cmpt]
+		upper = fvMatrix.data.upper_[cmpt]
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		resultArray = np.empty(nCells, dtype = float)
+		
+		resultArray = diag * psi
+		
+		for faceIndex in range(nInternalFaces):
+			
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			resultArray[ownIndex] += upper[faceIndex] * psi[neiIndex]
+			resultArray[neiIndex] += lower[faceIndex] * psi[ownIndex]
+			
+		return resultArray
+		
+	
+	@classmethod
+	def normFactor(self, Apsi, fvMatrix, cmpt):
+		
+		psi = fvMatrix.data.psi_
+		
+		mesh = fvMatrix.data.mesh_
+		
+		source = fvMatrix.data.source_
+		
+		
+		sumA = self.sumA(fvMatrix, cmpt)
+		
+		sumA *= self.gAverage(psi, cmpt)
+		
+		return (self.gSum													   \
+				   (														   \
+						(np.absolute(Apsi - sumA) + np.absolute(source[cmpt] - sumA)),\
+						mesh.geometryData.V_								   \
+				   ) + 1.0e-15 )
+	
+	
+	def sumA(fvMatrix, cmpt):
+		
+		mesh = fvMatrix.data.mesh_
+		
+		nCells =  mesh.geometryData.meshSize_
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+
+		lower = fvMatrix.data.lower_
+		diag = fvMatrix.data.diag_
+		upper = fvMatrix.data.upper_
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		resultArray = np.empty(nCells, dtype = float)
+		
+		# Vectorised
+		resultArray = np.copy(diag[cmpt])
+		
+		for faceIndex in range(nInternalFaces):
+			
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			resultArray[ownIndex] += upper[cmpt][faceIndex]
+			resultArray[neiIndex] += lower[cmpt][faceIndex]
+		
+		return resultArray
+	
+	
+	def gAverage(psi, cmpt):
+		
+		mesh = psi.data.mesh_
+		
+		psiValues = psi.data.cellValues_
+		
+		V = mesh.geometryData.V_
+		
+		nCells =  mesh.geometryData.meshSize_
+		
+		averageV = np.average(V)
+		
+		result = np.empty(nCells, dtype = float)
+		
+		# Vectorised
+		result = psiValues[cmpt] * V / averageV
+		
+		psiAverage = np.average(result)
+		
+		return psiAverage
+		
+	
+	def gSum(field, scalingField):
+		
+		nCells = np.size(field)
+		
+		scalingFieldAverage = np.average(scalingField)
+		
+		sum = np.sum(field * scalingField / scalingFieldAverage)
+		
+		return sum
+	
+	
+	def gSumMag(field, scalingField):
+		
+		nCells = np.size(field)
+		
+		scalingFieldAverage = np.average(scalingField)
+		
+		sum = np.sum(np.absolute(field * scalingField) / scalingFieldAverage)
+		
+		return sum
+		
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvSolution/fvSolution_cy.pyx b/foamPython/src/finiteVolume/fvSolution/fvSolution_cy.pyx
new file mode 100644
index 0000000000000000000000000000000000000000..e4e37e6623bfe47bc7689c08ce576853bf295b03
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvSolution/fvSolution_cy.pyx
@@ -0,0 +1,226 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the functions needed to solve the fvMatrix, with Cython
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+cimport numpy as np
+
+class fvSolution():
+	
+	# Default solution parameters
+	minIterDefault_ 	= 0
+	maxIterDefault_ 	= 1000
+	toleranceDefault_	= 0
+	relTolDefault_		= 0
+	
+	"""----------------------- Other functions ------------------------------"""		
+	@classmethod
+	def calculateResidual(self, fvMatrix, cmpt, normFactor):
+			
+		mesh = fvMatrix.data.mesh_
+		
+		psi = fvMatrix.data.psi_.data.cellValues_[cmpt]
+		
+		# Total number of cells
+		nCells =  mesh.geometryData.meshSize_
+		# Number of internal faces
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+
+		source = fvMatrix.data.source_[cmpt]
+		
+		lower = fvMatrix.data.lower_[cmpt]
+		diag = fvMatrix.data.diag_[cmpt]
+		upper = fvMatrix.data.upper_[cmpt]
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		# Calculate Apsi
+		Apsi = self.Amul(fvMatrix, cmpt)
+		
+		# Calculate normalisation factor for the first iteration
+		if (normFactor == None):
+			normFactor = self.normFactor(Apsi, fvMatrix, cmpt)
+			
+		# Initial residual guess
+		resArray = np.absolute(source - Apsi)
+		
+		# Geometric sum of residual guess magnitudes in all cells
+		resSum = self.gSumMag(resArray, mesh.geometryData.V_)
+		
+		return resSum/normFactor, normFactor
+	
+	
+	def checkConvergence(residual, resInit, tolerance, relTol):
+		
+		if (residual > tolerance and residual/resInit > relTol):
+			
+			return False
+		
+		else:
+			
+			return True
+	
+	
+	def Amul(fvMatrix, int cmpt):
+		
+		"""------------------- Cython declarations --------------------------"""
+		cdef int nCells
+		cdef int nInternalFaces
+		cdef int faceIndex
+		cdef int ownIndex
+		cdef int neiIndex
+		
+		cdef np.ndarray[np.float_t, ndim=1] psi
+		cdef np.ndarray[np.float_t, ndim=1] lower
+		cdef np.ndarray[np.float_t, ndim=1] diag
+		cdef np.ndarray[np.float_t, ndim=1] upper
+		cdef np.ndarray[np.int_t, ndim=1] owner
+		cdef np.ndarray[np.int_t, ndim=1] neighbour
+		cdef np.ndarray[np.float_t, ndim=1] resultArray
+		"""------------------------------------------------------------------"""
+		
+		mesh = fvMatrix.data.mesh_
+		
+		psi = fvMatrix.data.psi_.data.cellValues_[cmpt]
+		
+		nCells =  mesh.geometryData.meshSize_
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		
+		lower = fvMatrix.data.lower_[cmpt]
+		diag = fvMatrix.data.diag_[cmpt]
+		upper = fvMatrix.data.upper_[cmpt]
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		resultArray = np.empty(nCells, dtype = float)
+		
+		resultArray = diag * psi
+		
+		for faceIndex in range(nInternalFaces):
+			
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			resultArray[ownIndex] += upper[faceIndex] * psi[neiIndex]
+			resultArray[neiIndex] += lower[faceIndex] * psi[ownIndex]
+			
+		return resultArray
+		
+	
+	@classmethod
+	def normFactor(self, Apsi, fvMatrix, cmpt):
+		
+		psi = fvMatrix.data.psi_
+		
+		mesh = fvMatrix.data.mesh_
+		
+		source = fvMatrix.data.source_
+		
+		
+		sumA = self.sumA(fvMatrix, cmpt)
+		
+		sumA *= self.gAverage(psi, cmpt)
+		
+		return (self.gSum													   \
+				   (														   \
+						(np.absolute(Apsi - sumA) + np.absolute(source[cmpt] - sumA)),\
+						mesh.geometryData.V_								   \
+				   ) + 1.0e-15 )
+	
+	
+	def sumA(fvMatrix, cmpt):
+		
+		mesh = fvMatrix.data.mesh_
+		
+		nCells =  mesh.geometryData.meshSize_
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+
+		lower = fvMatrix.data.lower_
+		diag = fvMatrix.data.diag_
+		upper = fvMatrix.data.upper_
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		resultArray = np.empty(nCells, dtype = float)
+		
+		# Vectorised
+		resultArray = np.copy(diag[cmpt])
+		
+		for faceIndex in range(nInternalFaces):
+			
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			resultArray[ownIndex] += upper[cmpt][faceIndex]
+			resultArray[neiIndex] += lower[cmpt][faceIndex]
+		
+		return resultArray
+	
+	
+	def gAverage(psi, cmpt):
+		
+		mesh = psi.data.mesh_
+		
+		psiValues = psi.data.cellValues_
+		
+		V = mesh.geometryData.V_
+		
+		nCells =  mesh.geometryData.meshSize_
+		
+		averageV = np.average(V)
+		
+		result = np.empty(nCells, dtype = float)
+		
+		# Vectorised
+		result = psiValues[cmpt] * V / averageV
+		
+		psiAverage = np.average(result)
+		
+		return psiAverage
+		
+	
+	def gSum(field, scalingField):
+		
+		nCells = np.size(field)
+		
+		scalingFieldAverage = np.average(scalingField)
+		
+		sum = np.sum(field * scalingField / scalingFieldAverage)
+		
+		return sum
+	
+	
+	def gSumMag(field, scalingField):
+		
+		nCells = np.size(field)
+		
+		scalingFieldAverage = np.average(scalingField)
+		
+		sum = np.sum(np.absolute(field * scalingField) / scalingFieldAverage)
+		
+		return sum
+		
+		
+		
+		
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvSolution/fvSolution_py.py b/foamPython/src/finiteVolume/fvSolution/fvSolution_py.py
new file mode 100644
index 0000000000000000000000000000000000000000..635d5fd7a9a692fe587e5a9dd598a0a995f397f9
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvSolution/fvSolution_py.py
@@ -0,0 +1,207 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Class which contains the functions needed to solve the fvMatrix, pure Python
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+class fvSolution():
+	
+	# Default solution parameters
+	minIterDefault_ 	= 0
+	maxIterDefault_ 	= 1000
+	toleranceDefault_	= 0
+	relTolDefault_		= 0
+	
+	"""----------------------- Other functions ------------------------------"""		
+	@classmethod
+	def calculateResidual(self, fvMatrix, cmpt, normFactor):
+			
+		mesh = fvMatrix.data.mesh_
+		
+		psi = fvMatrix.data.psi_.data.cellValues_[cmpt]
+		
+		# Total number of cells
+		nCells =  mesh.geometryData.meshSize_
+		# Number of internal faces
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+
+		source = fvMatrix.data.source_[cmpt]
+		
+		lower = fvMatrix.data.lower_[cmpt]
+		diag = fvMatrix.data.diag_[cmpt]
+		upper = fvMatrix.data.upper_[cmpt]
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		# Calculate Apsi
+		Apsi = self.Amul(fvMatrix, cmpt)
+		
+		# Calculate normalisation factor for the first iteration
+		if (normFactor == None):
+			normFactor = self.normFactor(Apsi, fvMatrix, cmpt)
+			
+		# Initial residual guess
+		resArray = np.absolute(source - Apsi)
+		
+		# Geometric sum of residual guess magnitudes in all cells
+		resSum = self.gSumMag(resArray, mesh.geometryData.V_)
+		
+		return resSum/normFactor, normFactor
+	
+	
+	def checkConvergence(residual, resInit, tolerance, relTol):
+		
+		if (residual > tolerance and residual/resInit > relTol):
+			
+			return False
+		
+		else:
+			
+			return True
+	
+	
+	def Amul(fvMatrix, cmpt):
+		
+		mesh = fvMatrix.data.mesh_
+		
+		psi = fvMatrix.data.psi_.data.cellValues_[cmpt]
+		
+		nCells =  mesh.geometryData.meshSize_
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+		
+		source = fvMatrix.data.source_[cmpt]
+		lower = fvMatrix.data.lower_[cmpt]
+		diag = fvMatrix.data.diag_[cmpt]
+		upper = fvMatrix.data.upper_[cmpt]
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		resultArray = np.empty(nCells, dtype = float)
+		
+		resultArray = diag * psi
+		
+		for faceIndex in range(nInternalFaces):
+			
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			resultArray[ownIndex] += upper[faceIndex] * psi[neiIndex]
+			resultArray[neiIndex] += lower[faceIndex] * psi[ownIndex]
+			
+		return resultArray
+		
+	
+	@classmethod
+	def normFactor(self, Apsi, fvMatrix, cmpt):
+		
+		psi = fvMatrix.data.psi_
+		
+		mesh = fvMatrix.data.mesh_
+		
+		source = fvMatrix.data.source_
+		
+		
+		sumA = self.sumA(fvMatrix, cmpt)
+		
+		sumA *= self.gAverage(psi, cmpt)
+		
+		return (self.gSum													   \
+				   (														   \
+						(np.absolute(Apsi - sumA) + np.absolute(source[cmpt] - sumA)),\
+						mesh.geometryData.V_								   \
+				   ) + 1.0e-15 )
+	
+	
+	def sumA(fvMatrix, cmpt):
+		
+		mesh = fvMatrix.data.mesh_
+		
+		nCells =  mesh.geometryData.meshSize_
+		nInternalFaces = np.size(mesh.connectivityData.neighbour_)
+
+		lower = fvMatrix.data.lower_
+		diag = fvMatrix.data.diag_
+		upper = fvMatrix.data.upper_
+		
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		
+		resultArray = np.empty(nCells, dtype = float)
+		
+		# Vectorised
+		resultArray = np.copy(diag[cmpt])
+		
+		for faceIndex in range(nInternalFaces):
+			
+			ownIndex = owner[faceIndex]
+			neiIndex = neighbour[faceIndex]
+			
+			resultArray[ownIndex] += upper[cmpt][faceIndex]
+			resultArray[neiIndex] += lower[cmpt][faceIndex]
+		
+		return resultArray
+	
+	
+	def gAverage(psi, cmpt):
+		
+		mesh = psi.data.mesh_
+		
+		psiValues = psi.data.cellValues_
+		
+		V = mesh.geometryData.V_
+		
+		nCells =  mesh.geometryData.meshSize_
+		
+		averageV = np.average(V)
+		
+		result = np.empty(nCells, dtype = float)
+		
+		# Vectorised
+		result = psiValues[cmpt] * V / averageV
+		
+		psiAverage = np.average(result)
+		
+		return psiAverage
+		
+	
+	def gSum(field, scalingField):
+		
+		nCells = np.size(field)
+		
+		scalingFieldAverage = np.average(scalingField)
+		
+		sum = np.sum(field * scalingField / scalingFieldAverage)
+		
+		return sum
+	
+	
+	def gSumMag(field, scalingField):
+		
+		nCells = np.size(field)
+		
+		scalingFieldAverage = np.average(scalingField)
+		
+		sum = np.sum(np.absolute(field * scalingField) / scalingFieldAverage)
+		
+		return sum
+		
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvSolution/include.py b/foamPython/src/finiteVolume/fvSolution/include.py
new file mode 100644
index 0000000000000000000000000000000000000000..803e5035277c25762d8a7cfa31526fe4795c18aa
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvSolution/include.py
@@ -0,0 +1,24 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Include file for the implemented solvers
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+from src.finiteVolume.fvSolution.solvers.GaussSeidel.GaussSeidel import *
+from src.finiteVolume.fvSolution.solvers.PointJacobi.PointJacobi import *
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvSolution/setup_fvSolution.py b/foamPython/src/finiteVolume/fvSolution/setup_fvSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..b50e1bb75792d08ec2a7928aa4bf9c1adf9f8f7b
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvSolution/setup_fvSolution.py
@@ -0,0 +1,31 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Setup file for Cythonizing the fvSolution class
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import os
+
+from distutils.core import setup
+from Cython.Build import cythonize
+
+directives = {'language_level': 3}
+
+currDir = os.path.dirname(os.path.abspath(__file__))
+setup(ext_modules = cythonize(currDir + '/fvSolution.pyx', compiler_directives=directives))
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/GaussSeidel.py b/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/GaussSeidel.py
new file mode 100644
index 0000000000000000000000000000000000000000..e12f3675fc28f816b5ff1896d0f30a185d7e2dd5
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/GaussSeidel.py
@@ -0,0 +1,153 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Gauss-Seidel solver class, pure Python.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+from src.finiteVolume.fvSolution.fvSolution import *
+
+class GaussSeidel(fvSolution):
+	
+	@classmethod
+	def solveMatrix(self, fvMatrix, fvSolutionParameters):
+		
+		"""------------------ Data needed for the solver --------------------"""
+		maxIter = self.maxIterDefault_
+		minIter = self.minIterDefault_
+		tolerance = self.toleranceDefault_
+		relTol = self.relTolDefault_
+		
+		# Read solution parameters if they are present
+		try:
+			maxIter = fvSolutionParameters['maxIter']
+		except:
+			None
+		try:
+			minIter = fvSolutionParameters['minIter']
+		except:
+			None
+		try:
+			tolerance = fvSolutionParameters['tolerance']
+		except:
+			None
+		try:
+			relTol = fvSolutionParameters['relTol']
+		except:
+			None
+		
+		mesh = fvMatrix.data.mesh_
+		
+		noComponents = fvMatrix.data.psi_.noComponents_
+		
+		equationName = fvMatrix.data.psi_.data.fieldName_
+		
+		nCells = mesh.geometryData.meshSize_
+		
+		psi = fvMatrix.data.psi_.data.cellValues_
+		
+		source = fvMatrix.data.source_
+		
+		lower = fvMatrix.data.lower_
+		diag = fvMatrix.data.diag_
+		upper = fvMatrix.data.upper_
+
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		rowStart = fvMatrix.data.rowStart_
+		
+		"""------------------------- Solving loop ---------------------------"""
+		
+		for cmpt in range(noComponents):
+			
+			componentName = fvMatrix.data.psi_.componentsNames_[cmpt]
+			
+			normFactor = None
+			
+			resInit, normFactor = self.calculateResidual(fvMatrix, cmpt, normFactor)
+			
+			print("Gauss Seidel: Solving for " + equationName + componentName +		   \
+					", Initial residual = " + str(resInit) + ", ", end='')
+			
+			currentResidual = resInit
+			
+			noIter = 0
+			
+			if (resInit > 0.0):
+			
+				if ((minIter > 0) or not self.checkConvergence(currentResidual, resInit, tolerance, relTol)):
+					
+					while ((													   \
+						(noIter < maxIter)										   \
+						 and not self.checkConvergence(currentResidual, resInit, tolerance, relTol))
+						 or noIter < minIter):
+						
+						bPrime = np.copy(source[cmpt])
+						
+						fEnd = rowStart[0]
+						
+						# Loop over the matrix rows
+						for cellIndex in range(nCells):
+							
+							"""-----------------------------------------"""
+							# # Naive implementation
+							# psi[cmpt][cellIndex] = source[cmpt][cellIndex]
+							
+							# for i in range(neighbour.size):
+								# if (owner[i] == cellIndex):
+									# psi[cmpt][cellIndex] -= upper[cmpt][i] * psi[cmpt][neighbour[i]]
+								# elif (neighbour[i] == cellIndex):
+									# psi[cmpt][cellIndex] -= lower[cmpt][i] * psi[cmpt][owner[i]]
+									
+							# psi[cmpt][cellIndex] /= diag[cmpt][cellIndex]
+							
+							# psi[cmpt][cellIndex] += (1.0 - alpha) * psiOld[cmpt][cellIndex]
+							"""-----------------------------------------"""
+							 
+							# Efficient implementation
+							# Start and end of the owner array for this row
+							fStart = fEnd
+							fEnd = rowStart[cellIndex + 1]
+
+							# Get the accumulated neighbour side
+							psii = bPrime[cellIndex]
+							
+							# Accumulate the owner product side
+							for faceIndex in range(fStart, fEnd):
+								psii -= upper[cmpt][faceIndex] * psi[cmpt][neighbour[faceIndex]]
+
+							# Finish psi for this cell
+							psii /= diag[cmpt][cellIndex]
+
+							# Distribute the neighbour side using psi for this cell
+							for faceIndex in range(fStart, fEnd):
+								bPrime[neighbour[faceIndex]] -=					   \
+									lower[cmpt][faceIndex] * psii
+							
+							psi[cmpt][cellIndex] = psii
+						
+						# Increment inner iteration counter
+						noIter += 1
+						# Calculate the new residual
+						currentResidual, normFactor = self.calculateResidual(fvMatrix, cmpt, normFactor)
+			
+			print("Final residual = " + str(currentResidual) + ", No Iterations " + str(noIter))
+
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/GaussSeidel.py.orig b/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/GaussSeidel.py.orig
new file mode 100644
index 0000000000000000000000000000000000000000..e12f3675fc28f816b5ff1896d0f30a185d7e2dd5
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/GaussSeidel.py.orig
@@ -0,0 +1,153 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Gauss-Seidel solver class, pure Python.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+from src.finiteVolume.fvSolution.fvSolution import *
+
+class GaussSeidel(fvSolution):
+	
+	@classmethod
+	def solveMatrix(self, fvMatrix, fvSolutionParameters):
+		
+		"""------------------ Data needed for the solver --------------------"""
+		maxIter = self.maxIterDefault_
+		minIter = self.minIterDefault_
+		tolerance = self.toleranceDefault_
+		relTol = self.relTolDefault_
+		
+		# Read solution parameters if they are present
+		try:
+			maxIter = fvSolutionParameters['maxIter']
+		except:
+			None
+		try:
+			minIter = fvSolutionParameters['minIter']
+		except:
+			None
+		try:
+			tolerance = fvSolutionParameters['tolerance']
+		except:
+			None
+		try:
+			relTol = fvSolutionParameters['relTol']
+		except:
+			None
+		
+		mesh = fvMatrix.data.mesh_
+		
+		noComponents = fvMatrix.data.psi_.noComponents_
+		
+		equationName = fvMatrix.data.psi_.data.fieldName_
+		
+		nCells = mesh.geometryData.meshSize_
+		
+		psi = fvMatrix.data.psi_.data.cellValues_
+		
+		source = fvMatrix.data.source_
+		
+		lower = fvMatrix.data.lower_
+		diag = fvMatrix.data.diag_
+		upper = fvMatrix.data.upper_
+
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		rowStart = fvMatrix.data.rowStart_
+		
+		"""------------------------- Solving loop ---------------------------"""
+		
+		for cmpt in range(noComponents):
+			
+			componentName = fvMatrix.data.psi_.componentsNames_[cmpt]
+			
+			normFactor = None
+			
+			resInit, normFactor = self.calculateResidual(fvMatrix, cmpt, normFactor)
+			
+			print("Gauss Seidel: Solving for " + equationName + componentName +		   \
+					", Initial residual = " + str(resInit) + ", ", end='')
+			
+			currentResidual = resInit
+			
+			noIter = 0
+			
+			if (resInit > 0.0):
+			
+				if ((minIter > 0) or not self.checkConvergence(currentResidual, resInit, tolerance, relTol)):
+					
+					while ((													   \
+						(noIter < maxIter)										   \
+						 and not self.checkConvergence(currentResidual, resInit, tolerance, relTol))
+						 or noIter < minIter):
+						
+						bPrime = np.copy(source[cmpt])
+						
+						fEnd = rowStart[0]
+						
+						# Loop over the matrix rows
+						for cellIndex in range(nCells):
+							
+							"""-----------------------------------------"""
+							# # Naive implementation
+							# psi[cmpt][cellIndex] = source[cmpt][cellIndex]
+							
+							# for i in range(neighbour.size):
+								# if (owner[i] == cellIndex):
+									# psi[cmpt][cellIndex] -= upper[cmpt][i] * psi[cmpt][neighbour[i]]
+								# elif (neighbour[i] == cellIndex):
+									# psi[cmpt][cellIndex] -= lower[cmpt][i] * psi[cmpt][owner[i]]
+									
+							# psi[cmpt][cellIndex] /= diag[cmpt][cellIndex]
+							
+							# psi[cmpt][cellIndex] += (1.0 - alpha) * psiOld[cmpt][cellIndex]
+							"""-----------------------------------------"""
+							 
+							# Efficient implementation
+							# Start and end of the owner array for this row
+							fStart = fEnd
+							fEnd = rowStart[cellIndex + 1]
+
+							# Get the accumulated neighbour side
+							psii = bPrime[cellIndex]
+							
+							# Accumulate the owner product side
+							for faceIndex in range(fStart, fEnd):
+								psii -= upper[cmpt][faceIndex] * psi[cmpt][neighbour[faceIndex]]
+
+							# Finish psi for this cell
+							psii /= diag[cmpt][cellIndex]
+
+							# Distribute the neighbour side using psi for this cell
+							for faceIndex in range(fStart, fEnd):
+								bPrime[neighbour[faceIndex]] -=					   \
+									lower[cmpt][faceIndex] * psii
+							
+							psi[cmpt][cellIndex] = psii
+						
+						# Increment inner iteration counter
+						noIter += 1
+						# Calculate the new residual
+						currentResidual, normFactor = self.calculateResidual(fvMatrix, cmpt, normFactor)
+			
+			print("Final residual = " + str(currentResidual) + ", No Iterations " + str(noIter))
+
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/GaussSeidel_cy.pyx b/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/GaussSeidel_cy.pyx
new file mode 100644
index 0000000000000000000000000000000000000000..e164cc6139ec87c0a3adb6f24726db5b038d94ac
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/GaussSeidel_cy.pyx
@@ -0,0 +1,182 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Gauss-Seidel solver class, with Cython.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+cimport numpy as np
+
+from src.finiteVolume.fvSolution.fvSolution import *
+
+class GaussSeidel(fvSolution):
+	
+	@classmethod
+	def solveMatrix(self, fvMatrix, fvSolutionParameters):
+		
+		"""------------------- Cython declarations --------------------------"""
+		cdef int maxIter
+		cdef int minIter
+		cdef float tolerance
+		cdef float relTol
+		cdef int noComponents
+		cdef int nCells
+		cdef int cmpt
+		cdef float resInit
+		cdef float currentResidual
+		cdef int fStart
+		cdef int fEnd
+		cdef int noIter
+		cdef int cellIndex
+		cdef float psii
+		cdef int faceIndex
+		
+		cdef np.ndarray[np.float_t, ndim=1] psi
+		cdef np.ndarray[np.float_t, ndim=1] bPrime
+		cdef np.ndarray[np.float_t, ndim=1] source
+		cdef np.ndarray[np.float_t, ndim=1] lower
+		cdef np.ndarray[np.float_t, ndim=1] diag
+		cdef np.ndarray[np.float_t, ndim=1] upper
+		cdef np.ndarray[np.int_t, ndim=1] owner
+		cdef np.ndarray[np.int_t, ndim=1] neighbour
+		cdef np.ndarray[np.int_t, ndim=1] rowStart
+		"""------------------------------------------------------------------"""
+		
+		"""------------------ Data needed for the solver --------------------"""
+		maxIter = self.maxIterDefault_
+		minIter = self.minIterDefault_
+		tolerance = self.toleranceDefault_
+		relTol = self.relTolDefault_
+		
+		# Read solution parameters if they are present
+		try:
+			maxIter = fvSolutionParameters['maxIter']
+		except:
+			None
+		try:
+			minIter = fvSolutionParameters['minIter']
+		except:
+			None
+		try:
+			tolerance = fvSolutionParameters['tolerance']
+		except:
+			None
+		try:
+			relTol = fvSolutionParameters['relTol']
+		except:
+			None
+		
+		mesh = fvMatrix.data.mesh_
+		
+		noComponents = fvMatrix.data.psi_.noComponents_
+		
+		equationName = fvMatrix.data.psi_.data.fieldName_
+		
+		nCells = mesh.geometryData.meshSize_
+
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		rowStart = fvMatrix.data.rowStart_
+		
+		"""------------------------- Solving loop ---------------------------"""
+		
+		for cmpt in range(noComponents):
+			
+			psi = fvMatrix.data.psi_.data.cellValues_[cmpt]
+			
+			source = fvMatrix.data.source_[cmpt]
+			
+			lower = fvMatrix.data.lower_[cmpt]
+			diag = fvMatrix.data.diag_[cmpt]
+			upper = fvMatrix.data.upper_[cmpt]
+			
+			componentName = fvMatrix.data.psi_.componentsNames_[cmpt]
+			
+			normFactor = None
+			
+			resInit, normFactor = self.calculateResidual(fvMatrix, cmpt, normFactor)
+			
+			print("Gauss Seidel: Solving for " + equationName + componentName +		   \
+					", Initial residual = " + str(resInit) + ", ", end='')
+			
+			currentResidual = resInit
+			
+			noIter = 0
+			
+			if (resInit > 0.0):
+			
+				if ((minIter > 0) or not self.checkConvergence(currentResidual, resInit, tolerance, relTol)):
+					
+					while ((													   \
+						(noIter < maxIter)										   \
+						 and not self.checkConvergence(currentResidual, resInit, tolerance, relTol))
+						 or noIter < minIter):
+						
+						bPrime = np.copy(source)
+						
+						fEnd = rowStart[0]
+						
+						# Loop over the matrix rows
+						for cellIndex in range(nCells):
+							
+							"""----------------------------------------------"""
+							# # Naive implementation
+							# psi[cellIndex] = source[cellIndex]
+							
+							# for i in range(neighbour.size):
+								# if (owner[i] == cellIndex):
+									# psi[cellIndex] -= upper[i] * psi[neighbour[i]]
+								# elif (neighbour[i] == cellIndex):
+									# psi[cellIndex] -= lower[i] * psi[owner[i]]
+									
+							# psi[cellIndex] /= diag[cellIndex]
+							
+							# psi[cellIndex] += (1.0 - alpha) * psiOld[cellIndex]
+							"""----------------------------------------------"""
+							# Efficient implementation
+							# Start and end of the owner array for this row
+							fStart = fEnd
+							fEnd = rowStart[cellIndex + 1]
+
+							# Get the accumulated neighbour side
+							psii = bPrime[cellIndex]
+							
+							# Accumulate the owner product side
+							for faceIndex in range(fStart, fEnd):
+								psii -= upper[faceIndex] * psi[neighbour[faceIndex]]
+
+							# Finish psi for this cell
+							psii /= diag[cellIndex]
+
+							# Distribute the neighbour side using psi for this cell
+							for faceIndex in range(fStart, fEnd):
+								bPrime[neighbour[faceIndex]] -=					   \
+									lower[faceIndex] * psii
+							
+							psi[cellIndex] = psii
+							"""----------------------------------------------"""
+						
+						# Increment inner iteration counter
+						noIter += 1
+						# Calculate the new residual
+						currentResidual, normFactor = self.calculateResidual(fvMatrix, cmpt, normFactor)
+			
+			print("Final residual = " + str(currentResidual) + ", No Iterations " + str(noIter))
+
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/GaussSeidel_py.py b/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/GaussSeidel_py.py
new file mode 100644
index 0000000000000000000000000000000000000000..e12f3675fc28f816b5ff1896d0f30a185d7e2dd5
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/GaussSeidel_py.py
@@ -0,0 +1,153 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Gauss-Seidel solver class, pure Python.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+from src.finiteVolume.fvSolution.fvSolution import *
+
+class GaussSeidel(fvSolution):
+	
+	@classmethod
+	def solveMatrix(self, fvMatrix, fvSolutionParameters):
+		
+		"""------------------ Data needed for the solver --------------------"""
+		maxIter = self.maxIterDefault_
+		minIter = self.minIterDefault_
+		tolerance = self.toleranceDefault_
+		relTol = self.relTolDefault_
+		
+		# Read solution parameters if they are present
+		try:
+			maxIter = fvSolutionParameters['maxIter']
+		except:
+			None
+		try:
+			minIter = fvSolutionParameters['minIter']
+		except:
+			None
+		try:
+			tolerance = fvSolutionParameters['tolerance']
+		except:
+			None
+		try:
+			relTol = fvSolutionParameters['relTol']
+		except:
+			None
+		
+		mesh = fvMatrix.data.mesh_
+		
+		noComponents = fvMatrix.data.psi_.noComponents_
+		
+		equationName = fvMatrix.data.psi_.data.fieldName_
+		
+		nCells = mesh.geometryData.meshSize_
+		
+		psi = fvMatrix.data.psi_.data.cellValues_
+		
+		source = fvMatrix.data.source_
+		
+		lower = fvMatrix.data.lower_
+		diag = fvMatrix.data.diag_
+		upper = fvMatrix.data.upper_
+
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		rowStart = fvMatrix.data.rowStart_
+		
+		"""------------------------- Solving loop ---------------------------"""
+		
+		for cmpt in range(noComponents):
+			
+			componentName = fvMatrix.data.psi_.componentsNames_[cmpt]
+			
+			normFactor = None
+			
+			resInit, normFactor = self.calculateResidual(fvMatrix, cmpt, normFactor)
+			
+			print("Gauss Seidel: Solving for " + equationName + componentName +		   \
+					", Initial residual = " + str(resInit) + ", ", end='')
+			
+			currentResidual = resInit
+			
+			noIter = 0
+			
+			if (resInit > 0.0):
+			
+				if ((minIter > 0) or not self.checkConvergence(currentResidual, resInit, tolerance, relTol)):
+					
+					while ((													   \
+						(noIter < maxIter)										   \
+						 and not self.checkConvergence(currentResidual, resInit, tolerance, relTol))
+						 or noIter < minIter):
+						
+						bPrime = np.copy(source[cmpt])
+						
+						fEnd = rowStart[0]
+						
+						# Loop over the matrix rows
+						for cellIndex in range(nCells):
+							
+							"""-----------------------------------------"""
+							# # Naive implementation
+							# psi[cmpt][cellIndex] = source[cmpt][cellIndex]
+							
+							# for i in range(neighbour.size):
+								# if (owner[i] == cellIndex):
+									# psi[cmpt][cellIndex] -= upper[cmpt][i] * psi[cmpt][neighbour[i]]
+								# elif (neighbour[i] == cellIndex):
+									# psi[cmpt][cellIndex] -= lower[cmpt][i] * psi[cmpt][owner[i]]
+									
+							# psi[cmpt][cellIndex] /= diag[cmpt][cellIndex]
+							
+							# psi[cmpt][cellIndex] += (1.0 - alpha) * psiOld[cmpt][cellIndex]
+							"""-----------------------------------------"""
+							 
+							# Efficient implementation
+							# Start and end of the owner array for this row
+							fStart = fEnd
+							fEnd = rowStart[cellIndex + 1]
+
+							# Get the accumulated neighbour side
+							psii = bPrime[cellIndex]
+							
+							# Accumulate the owner product side
+							for faceIndex in range(fStart, fEnd):
+								psii -= upper[cmpt][faceIndex] * psi[cmpt][neighbour[faceIndex]]
+
+							# Finish psi for this cell
+							psii /= diag[cmpt][cellIndex]
+
+							# Distribute the neighbour side using psi for this cell
+							for faceIndex in range(fStart, fEnd):
+								bPrime[neighbour[faceIndex]] -=					   \
+									lower[cmpt][faceIndex] * psii
+							
+							psi[cmpt][cellIndex] = psii
+						
+						# Increment inner iteration counter
+						noIter += 1
+						# Calculate the new residual
+						currentResidual, normFactor = self.calculateResidual(fvMatrix, cmpt, normFactor)
+			
+			print("Final residual = " + str(currentResidual) + ", No Iterations " + str(noIter))
+
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/setup_GaussSeidel.py b/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/setup_GaussSeidel.py
new file mode 100644
index 0000000000000000000000000000000000000000..f25e427274934ab99b15b36d1f110700444517f9
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvSolution/solvers/GaussSeidel/setup_GaussSeidel.py
@@ -0,0 +1,31 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Setup file for Cythonizing the Gauss-Seidel solver
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import os
+
+from distutils.core import setup
+from Cython.Build import cythonize
+
+directives = {'language_level': 3}
+
+currDir = os.path.dirname(os.path.abspath(__file__))
+setup(ext_modules = cythonize(currDir + '/GaussSeidel.pyx', compiler_directives=directives))
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/src/finiteVolume/fvSolution/solvers/PointJacobi/PointJacobi.py b/foamPython/src/finiteVolume/fvSolution/solvers/PointJacobi/PointJacobi.py
new file mode 100644
index 0000000000000000000000000000000000000000..f31ae3369b7e9fd433dd61dd22cd4d63c491b086
--- /dev/null
+++ b/foamPython/src/finiteVolume/fvSolution/solvers/PointJacobi/PointJacobi.py
@@ -0,0 +1,125 @@
+"""
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |
+-------------------------------------------------------------------------------
+
+Author
+	Robert Anderluh, 2021
+	
+Description
+    Point Jacobi solver class.
+    
+\*---------------------------------------------------------------------------*/
+"""
+
+import numpy as np
+
+from src.finiteVolume.fvSolution.fvSolution import *
+	
+class PointJacobi(fvSolution):
+	
+	@classmethod
+	def solveMatrix(self, fvMatrix, fvSolutionParameters):
+		
+		"""------------------ Data needed for the solver --------------------"""
+		maxIter = self.maxIterDefault_
+		minIter = self.minIterDefault_
+		tolerance = self.toleranceDefault_
+		relTol = self.relTolDefault_
+		
+		# Read solution parameters if they are present
+		try:
+			maxIter = fvSolutionParameters['maxIter']
+		except:
+			None
+		try:
+			minIter = fvSolutionParameters['minIter']
+		except:
+			None
+		try:
+			tolerance = fvSolutionParameters['tolerance']
+		except:
+			None
+		try:
+			relTol = fvSolutionParameters['relTol']
+		except:
+			None
+		
+		mesh = fvMatrix.data.mesh_
+		
+		noComponents = fvMatrix.data.psi_.noComponents_
+		
+		equationName = fvMatrix.data.psi_.data.fieldName_
+		
+		nCells = mesh.geometryData.meshSize_
+		
+		psi = fvMatrix.data.psi_.data.cellValues_
+		
+		source = fvMatrix.data.source_
+		
+		lower = fvMatrix.data.lower_
+		diag = fvMatrix.data.diag_
+		upper = fvMatrix.data.upper_
+
+		owner = mesh.connectivityData.owner_
+		neighbour = mesh.connectivityData.neighbour_
+		rowStart = fvMatrix.data.rowStart_
+		
+		"""------------------------- Solving loop ---------------------------"""
+		
+		for cmpt in range(noComponents):
+			
+			componentName = fvMatrix.data.psi_.componentsNames_[cmpt]
+			
+			normFactor = None
+			
+			resInit, normFactor = self.calculateResidual(fvMatrix, cmpt, normFactor)
+			
+			print("Point Jacobi: Solving for " + equationName + componentName +		   \
+					", Initial residual = " + str(resInit) + ", ", end='')
+			
+			currentResidual = resInit
+			
+			if (resInit > 0.0):
+			
+				if ((minIter > 0) or not self.checkConvergence(currentResidual, resInit, tolerance, relTol)):
+					
+					noIter = 0
+					
+					while ((													   \
+						(noIter < maxIter)										   \
+						 and not self.checkConvergence(currentResidual, resInit, tolerance, relTol))
+						 or noIter < minIter):
+						
+						psiOld = np.copy(psi[cmpt])
+						
+						psi[cmpt] = np.copy(source[cmpt])
+							
+						# Loop through the internal faces and subtract neighbour
+						# contributions
+						for faceIndex in range(neighbour.size):
+							
+							psi[cmpt][owner[faceIndex]] -= upper[cmpt][faceIndex] *\
+								psiOld[neighbour[faceIndex]]
+								
+							psi[cmpt][neighbour[faceIndex]] -= lower[cmpt][faceIndex] *\
+								psiOld[owner[faceIndex]]
+						
+						# psi = 1 / ap * (b - a_n * psi_n)
+						psi[cmpt] /= diag[cmpt]
+						
+						# Increment inner iteration counter
+						noIter += 1
+						# Calculate the new residual
+						currentResidual, normFactor = self.calculateResidual(fvMatrix, cmpt, normFactor)
+			
+			print("Final residual = " + str(currentResidual) + ", No Iterations " + str(noIter))
+
+
+"""
+// ************************************************************************* //
+"""
diff --git a/foamPython/tutorials/icoFoam/cavity/Allclean b/foamPython/tutorials/icoFoam/cavity/Allclean
new file mode 100755
index 0000000000000000000000000000000000000000..b25723fb17908f9ab36d8ab00225197bdf24620f
--- /dev/null
+++ b/foamPython/tutorials/icoFoam/cavity/Allclean
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# Clean times
+rm -r [1-9]* > /dev/null 2>&1 ; rm -r 0.[0-9]* > /dev/null 2>&1
+
+# Clean mesh
+rm -r constant/polyMesh
diff --git a/foamPython/tutorials/icoFoam/cavity/Allrun b/foamPython/tutorials/icoFoam/cavity/Allrun
new file mode 100755
index 0000000000000000000000000000000000000000..c316861c09513cd8b6f30e52e3ba24142077373e
--- /dev/null
+++ b/foamPython/tutorials/icoFoam/cavity/Allrun
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+./Allrun.pre
+
+python3 ../../../icoFoam.py
diff --git a/foamPython/tutorials/icoFoam/cavity/Allrun.pre b/foamPython/tutorials/icoFoam/cavity/Allrun.pre
new file mode 100755
index 0000000000000000000000000000000000000000..65bbea57171fe0f0ba0155cbc6abd7db818e23a4
--- /dev/null
+++ b/foamPython/tutorials/icoFoam/cavity/Allrun.pre
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+blockMesh
diff --git a/foamPython/tutorials/icoFoam/cavity/caseSetup.py b/foamPython/tutorials/icoFoam/cavity/caseSetup.py
new file mode 100644
index 0000000000000000000000000000000000000000..397aa7de6ce7224b3d73636ffe75140207f4481c
--- /dev/null
+++ b/foamPython/tutorials/icoFoam/cavity/caseSetup.py
@@ -0,0 +1,141 @@
+# Case setup file for icoFoam.py
+
+"""--------------------------------------------------------------------------"""
+
+# 2D cavity case
+boundaryDefinition_U = {
+'movingWall': ( 'fixedValue'     	, np.array([1.0, 0.0, 0.0])), 
+'fixedWalls': ( 'fixedValue' 		, np.array([0.0, 0.0, 0.0])),
+'frontAndBack': ( 'empty'		    , None   )
+}
+
+boundaryDefinition_p = {
+'movingWall': ( 'fixedGradient'    	, np.array([0.0])), 
+'fixedWalls': ( 'fixedGradient' 	, np.array([0.0])),
+'frontAndBack': ( 'empty'		    , None)
+}
+
+"""--------------------------------------------------------------------------"""
+
+# # 3D cavity case
+# boundaryDefinition_U = {
+# 'movingWall': ( 'fixedValue'     	, np.array([1.0, 0.0, 0.0])), 
+# 'fixedWalls': ( 'fixedValue' 		, np.array([0.0, 0.0, 0.0])),
+# 'frontAndBack': ( 'fixedValue'     	, np.array([1.0, 0.0, 0.0])), 
+# }
+
+# boundaryDefinition_p = {
+# 'movingWall': ( 'fixedGradient'    	, np.array([0.0])), 
+# 'fixedWalls': ( 'fixedGradient' 	, np.array([0.0])),
+# 'frontAndBack': ( 'fixedGradient'    	, np.array([0.0])), 
+# }
+
+"""--------------------------------------------------------------------------"""
+
+# # 2D inlet-outlet cavity case
+# boundaryDefinition_U = {
+# 'inlet':		( 'fixedValue'     		, np.array([0.2, 0.0, 0.0])), 
+# # 'inlet':		( 'fixedGradient' 		, np.array([0.0, 0.0, 0.0])),
+# 'outlet':		( 'fixedGradient' 		, np.array([0.0, 0.0, 0.0])),
+# 'walls':		( 'fixedValue'   		, np.array([0.0, 0.0, 0.0])), 
+# 'frontAndBack':	( 'empty'				, None					   )
+# }
+
+# boundaryDefinition_p = {
+# 'inlet':		( 'fixedGradient' 		, np.array([0.0])),
+# # 'inlet':		( 'fixedValue'   		, np.array([1.84])), 
+# 'outlet':		( 'fixedValue'   		, np.array([0.0])), 
+# 'walls':		( 'fixedGradient' 		, np.array([0.0])), 
+# 'frontAndBack':	( 'empty'			    , None			 )
+# }
+
+"""--------------------------------------------------------------------------"""
+
+# # 3D inlet-outlet cavity case
+# boundaryDefinition_U = {
+# 'inlet':		( 'fixedValue'     		, np.array([0.2, 0.0, 0.0])),
+# 'outlet':		( 'fixedGradient' 		, np.array([0.0, 0.0, 0.0])),
+# 'walls':		( 'fixedValue'   		, np.array([0.0, 0.0, 0.0])), 
+# }
+
+# boundaryDefinition_p = {
+# 'inlet':		( 'fixedGradient' 		, np.array([0.0])),
+# 'outlet':		( 'fixedValue'   		, np.array([0.0])), 
+# 'walls':		( 'fixedGradient' 		, np.array([0.0])), 
+# }
+
+"""--------------------------------------------------------------------------"""
+
+# # Pitz Daily
+# boundaryDefinition_U = {
+# 'inlet':		( 'fixedValue'     		, np.array([1.0, 0.0, 0.0])), 
+# 'outlet':		( 'fixedGradient' 		, np.array([0.0, 0.0, 0.0])),
+# 'upperWall':	( 'fixedValue'   		, np.array([0.0, 0.0, 0.0])), 
+# 'lowerWall':	( 'fixedValue'   		, np.array([0.0, 0.0, 0.0])), 
+# 'frontAndBack':	( 'empty'				, None					   )
+# }
+
+# boundaryDefinition_p = {
+# 'inlet':		( 'fixedGradient' 		, np.array([0.0])),
+# 'outlet':		( 'fixedValue'   		, np.array([0.0])), 
+# 'upperWall':	( 'fixedGradient' 		, np.array([0.0])), 
+# 'lowerWall':	( 'fixedGradient' 		, np.array([0.0])), 
+# 'frontAndBack':	( 'empty'			    , None			 )
+# }
+
+"""--------------------------------------------------------------------------"""
+
+internalField_U = np.array([0.0, 0.0, 0.0])
+internalField_p = np.array([0.0])
+
+# Kinematic viscosity
+nu = 0.01
+
+# Time control
+startTime = 0.0
+
+writeTime = 0.0001
+
+deltaT = 0.0001
+
+endTime = 0.5
+
+# Finite volume schemes
+
+ddtScheme = "Euler"
+
+laplacianScheme = "linearOrthogonal"
+
+divUScheme = "upwind"
+
+gradPScheme = "linear"
+
+divHbyAScheme = "linear"
+
+# Update case setup if it is modified
+runTimeModifiable = True
+
+# fvSolution parameters
+fvSolution_U = {
+'solver'		:	'GaussSeidel',
+# 'solver'		:	'PointJacobi',
+# 'minIter'		:	0,
+# 'maxIter'		:	10,
+'tolerance'		:	1e-6,
+'relTol'		:	0
+}
+
+fvSolution_p = {
+'solver'		:	'GaussSeidel',
+# 'solver'		:	'PointJacobi',
+# 'minIter'		:	0,
+# 'maxIter'		:	10000,
+'tolerance'		:	1e-5,
+'relTol'		:	0,
+'refCell'		:	0,
+'refValue'		:	np.array([0.0])
+}
+
+PISO = {
+'nCorrectors'	:	3
+}
diff --git a/foamPython/tutorials/icoFoam/cavity/system/blockMeshDict b/foamPython/tutorials/icoFoam/cavity/system/blockMeshDict
new file mode 100644
index 0000000000000000000000000000000000000000..1a79ad776cc6d851e6138887058c512adc425ac3
--- /dev/null
+++ b/foamPython/tutorials/icoFoam/cavity/system/blockMeshDict
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |    
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      blockMeshDict;
+}
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+convertToMeters 1;
+
+vertices
+(
+    (0 0 0)
+    (0.1 0 0)
+    (0.1 0.1 0)
+    (0 0.1 0)
+    (0 0 0.01)
+    (0.1 0 0.01)
+    (0.1 0.1 0.01)
+    (0 0.1 0.01)
+);
+
+blocks
+(
+    hex (0 1 2 3 4 5 6 7) (20 20 1) simpleGrading (1 1 1)
+);
+
+edges
+(
+);
+
+boundary
+(
+    movingWall
+    {
+        type wall;
+        faces
+        (
+            (3 7 6 2)
+        );
+    }
+    fixedWalls
+    {
+        type wall;
+        faces
+        (
+            (0 4 7 3)
+            (2 6 5 1)
+            (1 5 4 0)
+        );
+    }
+    frontAndBack
+    {
+        type empty;
+        faces
+        (
+            (0 3 2 1)
+            (4 5 6 7)
+        );
+    }
+);
+
+mergePatchPairs
+(
+);
+
+// ************************************************************************* //
diff --git a/foamPython/tutorials/icoFoam/cavity/system/controlDict b/foamPython/tutorials/icoFoam/cavity/system/controlDict
new file mode 100644
index 0000000000000000000000000000000000000000..a0ecc3f9bace177d3d7288fad4407dbd68a0aa33
--- /dev/null
+++ b/foamPython/tutorials/icoFoam/cavity/system/controlDict
@@ -0,0 +1,26 @@
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |    
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      controlDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// NOTE: controlDict is needed for blockMesh or other utilities
+
+application     xyz;
+
+deltaT          123;
+
+writeInterval   123;
+
+
+// ************************************************************************* //
diff --git a/foamPython/tutorials/icoFoam/cavity/system/controlDict.orig b/foamPython/tutorials/icoFoam/cavity/system/controlDict.orig
new file mode 100644
index 0000000000000000000000000000000000000000..f8daff1dc2db5ea219196830d58f8486c60ffc0f
--- /dev/null
+++ b/foamPython/tutorials/icoFoam/cavity/system/controlDict.orig
@@ -0,0 +1,30 @@
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |    
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      controlDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+<<<<<<< HEAD
+application     icoFoam;
+=======
+// NOTE: controlDict is needed for blockMesh or other utilities
+>>>>>>> 232b4913096958141af633dad33808f9d2282eb7
+
+application     xyz;
+
+deltaT          123;
+
+writeInterval   123;
+
+
+// ************************************************************************* //
diff --git a/foamPython/tutorials/laplacianFoam/cavity/Allclean b/foamPython/tutorials/laplacianFoam/cavity/Allclean
new file mode 100755
index 0000000000000000000000000000000000000000..b25723fb17908f9ab36d8ab00225197bdf24620f
--- /dev/null
+++ b/foamPython/tutorials/laplacianFoam/cavity/Allclean
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# Clean times
+rm -r [1-9]* > /dev/null 2>&1 ; rm -r 0.[0-9]* > /dev/null 2>&1
+
+# Clean mesh
+rm -r constant/polyMesh
diff --git a/foamPython/tutorials/laplacianFoam/cavity/Allrun b/foamPython/tutorials/laplacianFoam/cavity/Allrun
new file mode 100755
index 0000000000000000000000000000000000000000..ed999c142f04c902c8bbebf2b8f46b7de6da016a
--- /dev/null
+++ b/foamPython/tutorials/laplacianFoam/cavity/Allrun
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+./Allrun.pre
+
+python3 ../../../laplacianFoam.py
diff --git a/foamPython/tutorials/laplacianFoam/cavity/Allrun.pre b/foamPython/tutorials/laplacianFoam/cavity/Allrun.pre
new file mode 100755
index 0000000000000000000000000000000000000000..65bbea57171fe0f0ba0155cbc6abd7db818e23a4
--- /dev/null
+++ b/foamPython/tutorials/laplacianFoam/cavity/Allrun.pre
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+blockMesh
diff --git a/foamPython/tutorials/laplacianFoam/cavity/caseSetup.py b/foamPython/tutorials/laplacianFoam/cavity/caseSetup.py
new file mode 100644
index 0000000000000000000000000000000000000000..b72119d20a1011e9ea10e2c732ec5e952b18f144
--- /dev/null
+++ b/foamPython/tutorials/laplacianFoam/cavity/caseSetup.py
@@ -0,0 +1,45 @@
+# Case setup file for laplacianFoam.py
+
+"""--------------------------------------------------------------------------"""
+
+# Cavity case
+boundaryDefinition_T = {
+'movingWall': ( 'fixedValue'     	, np.array([373.0])), 
+'fixedWalls': ( 'fixedValue' 		, np.array([273.0])),
+'frontAndBack': ( 'empty'		    , None   )
+}
+
+"""--------------------------------------------------------------------------"""
+
+internalField_T = np.array([273.0])
+
+# Conductivity
+DT = 2e-4
+
+# Time control
+startTime = 0.0
+
+writeTime = 0.1
+
+deltaT = 0.005
+
+endTime = 3.0
+
+# Finite volume schemes
+
+ddtScheme = "Euler"
+
+laplacianScheme = "linearOrthogonal"
+
+# Update case setup if it is modified
+runTimeModifiable = True
+
+# fvSolution parameters
+fvSolution_T = {
+'solver'		:	'GaussSeidel',
+# 'solver'		:	'PointJacobi',
+# 'minIter'		:	0,
+# 'maxIter'		:	5,
+'tolerance'		:	1e-6,
+# 'relTol'		:	0
+}
diff --git a/foamPython/tutorials/laplacianFoam/cavity/system/blockMeshDict b/foamPython/tutorials/laplacianFoam/cavity/system/blockMeshDict
new file mode 100644
index 0000000000000000000000000000000000000000..1a79ad776cc6d851e6138887058c512adc425ac3
--- /dev/null
+++ b/foamPython/tutorials/laplacianFoam/cavity/system/blockMeshDict
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |    
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      blockMeshDict;
+}
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+convertToMeters 1;
+
+vertices
+(
+    (0 0 0)
+    (0.1 0 0)
+    (0.1 0.1 0)
+    (0 0.1 0)
+    (0 0 0.01)
+    (0.1 0 0.01)
+    (0.1 0.1 0.01)
+    (0 0.1 0.01)
+);
+
+blocks
+(
+    hex (0 1 2 3 4 5 6 7) (20 20 1) simpleGrading (1 1 1)
+);
+
+edges
+(
+);
+
+boundary
+(
+    movingWall
+    {
+        type wall;
+        faces
+        (
+            (3 7 6 2)
+        );
+    }
+    fixedWalls
+    {
+        type wall;
+        faces
+        (
+            (0 4 7 3)
+            (2 6 5 1)
+            (1 5 4 0)
+        );
+    }
+    frontAndBack
+    {
+        type empty;
+        faces
+        (
+            (0 3 2 1)
+            (4 5 6 7)
+        );
+    }
+);
+
+mergePatchPairs
+(
+);
+
+// ************************************************************************* //
diff --git a/foamPython/tutorials/laplacianFoam/cavity/system/controlDict b/foamPython/tutorials/laplacianFoam/cavity/system/controlDict
new file mode 100644
index 0000000000000000000000000000000000000000..a0ecc3f9bace177d3d7288fad4407dbd68a0aa33
--- /dev/null
+++ b/foamPython/tutorials/laplacianFoam/cavity/system/controlDict
@@ -0,0 +1,26 @@
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |    
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      controlDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// NOTE: controlDict is needed for blockMesh or other utilities
+
+application     xyz;
+
+deltaT          123;
+
+writeInterval   123;
+
+
+// ************************************************************************* //
diff --git a/foamPython/tutorials/laplacianFoam/cavity/system/controlDict.orig b/foamPython/tutorials/laplacianFoam/cavity/system/controlDict.orig
new file mode 100644
index 0000000000000000000000000000000000000000..caf90835aa588c74e886c089963b01fbb3afb57a
--- /dev/null
+++ b/foamPython/tutorials/laplacianFoam/cavity/system/controlDict.orig
@@ -0,0 +1,30 @@
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |    
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      controlDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+<<<<<<< HEAD
+application     laplacianFoam;
+=======
+// NOTE: controlDict is needed for blockMesh or other utilities
+>>>>>>> 232b4913096958141af633dad33808f9d2282eb7
+
+application     xyz;
+
+deltaT          123;
+
+writeInterval   123;
+
+
+// ************************************************************************* //
diff --git a/foamPython/tutorials/simpleFoam/cavity/Allclean b/foamPython/tutorials/simpleFoam/cavity/Allclean
new file mode 100755
index 0000000000000000000000000000000000000000..b25723fb17908f9ab36d8ab00225197bdf24620f
--- /dev/null
+++ b/foamPython/tutorials/simpleFoam/cavity/Allclean
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# Clean times
+rm -r [1-9]* > /dev/null 2>&1 ; rm -r 0.[0-9]* > /dev/null 2>&1
+
+# Clean mesh
+rm -r constant/polyMesh
diff --git a/foamPython/tutorials/simpleFoam/cavity/Allrun b/foamPython/tutorials/simpleFoam/cavity/Allrun
new file mode 100755
index 0000000000000000000000000000000000000000..38f2c74e21199e24573d1badfe4a1545c0fd7add
--- /dev/null
+++ b/foamPython/tutorials/simpleFoam/cavity/Allrun
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+./Allrun.pre
+
+python3 ../../../simpleFoam.py
diff --git a/foamPython/tutorials/simpleFoam/cavity/Allrun.pre b/foamPython/tutorials/simpleFoam/cavity/Allrun.pre
new file mode 100755
index 0000000000000000000000000000000000000000..65bbea57171fe0f0ba0155cbc6abd7db818e23a4
--- /dev/null
+++ b/foamPython/tutorials/simpleFoam/cavity/Allrun.pre
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+blockMesh
diff --git a/foamPython/tutorials/simpleFoam/cavity/caseSetup.py b/foamPython/tutorials/simpleFoam/cavity/caseSetup.py
new file mode 100644
index 0000000000000000000000000000000000000000..e8fba1692c102b1c0bc22ee8cd38f55595059f2b
--- /dev/null
+++ b/foamPython/tutorials/simpleFoam/cavity/caseSetup.py
@@ -0,0 +1,72 @@
+# Case setup file for simpleFoam.py
+
+"""--------------------------------------------------------------------------"""
+
+# 2D cavity case
+boundaryDefinition_U = {
+'movingWall': ( 'fixedValue'     	, np.array([1.0, 0.0, 0.0])), 
+'fixedWalls': ( 'fixedValue' 		, np.array([0.0, 0.0, 0.0])),
+'frontAndBack': ( 'empty'		    , None   )
+}
+
+boundaryDefinition_p = {
+'movingWall': ( 'fixedGradient'    	, np.array([0.0])), 
+'fixedWalls': ( 'fixedGradient' 	, np.array([0.0])),
+'frontAndBack': ( 'empty'		    , None)
+}
+
+
+"""--------------------------------------------------------------------------"""
+
+internalField_U = np.array([0.0, 0.0, 0.0])
+internalField_p = np.array([0.0])
+
+# Kinematic viscosity
+nu = 0.01
+
+# Time control
+startTime = 0.0
+
+writeTime = 1
+
+deltaT = 1
+
+endTime = 50
+
+# Finite volume schemes
+
+ddtScheme = "steadyState"
+
+laplacianScheme = "linearOrthogonal"
+
+divUScheme = "upwind"
+
+gradPScheme = "linear"
+
+divHbyAScheme = "linear"
+
+# Update case setup if it is modified
+runTimeModifiable = True
+
+# fvSolution parameters
+fvSolution_U = {
+'solver'		:	'GaussSeidel',
+# 'solver'		:	'PointJacobi',
+# 'minIter'		:	0,
+# 'maxIter'		:	10,
+'tolerance'		:	1e-6,
+'relTol'		:	0.01,
+'impUR'			:	0.7
+}
+
+fvSolution_p = {
+'solver'		:	'GaussSeidel',
+# 'solver'		:	'PointJacobi',
+# 'minIter'		:	0,
+# 'maxIter'		:	100000,
+'tolerance'		:	1e-5,
+'relTol'		:	0.1,
+'expUR'			:	0.3,
+'refCell'		:	0,
+'refValue'		:	np.array([0.0])
+}
diff --git a/foamPython/tutorials/simpleFoam/cavity/system/blockMeshDict b/foamPython/tutorials/simpleFoam/cavity/system/blockMeshDict
new file mode 100644
index 0000000000000000000000000000000000000000..1a79ad776cc6d851e6138887058c512adc425ac3
--- /dev/null
+++ b/foamPython/tutorials/simpleFoam/cavity/system/blockMeshDict
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |    
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      blockMeshDict;
+}
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+convertToMeters 1;
+
+vertices
+(
+    (0 0 0)
+    (0.1 0 0)
+    (0.1 0.1 0)
+    (0 0.1 0)
+    (0 0 0.01)
+    (0.1 0 0.01)
+    (0.1 0.1 0.01)
+    (0 0.1 0.01)
+);
+
+blocks
+(
+    hex (0 1 2 3 4 5 6 7) (20 20 1) simpleGrading (1 1 1)
+);
+
+edges
+(
+);
+
+boundary
+(
+    movingWall
+    {
+        type wall;
+        faces
+        (
+            (3 7 6 2)
+        );
+    }
+    fixedWalls
+    {
+        type wall;
+        faces
+        (
+            (0 4 7 3)
+            (2 6 5 1)
+            (1 5 4 0)
+        );
+    }
+    frontAndBack
+    {
+        type empty;
+        faces
+        (
+            (0 3 2 1)
+            (4 5 6 7)
+        );
+    }
+);
+
+mergePatchPairs
+(
+);
+
+// ************************************************************************* //
diff --git a/foamPython/tutorials/simpleFoam/cavity/system/controlDict b/foamPython/tutorials/simpleFoam/cavity/system/controlDict
new file mode 100644
index 0000000000000000000000000000000000000000..a0ecc3f9bace177d3d7288fad4407dbd68a0aa33
--- /dev/null
+++ b/foamPython/tutorials/simpleFoam/cavity/system/controlDict
@@ -0,0 +1,26 @@
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |    
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      controlDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// NOTE: controlDict is needed for blockMesh or other utilities
+
+application     xyz;
+
+deltaT          123;
+
+writeInterval   123;
+
+
+// ************************************************************************* //
diff --git a/foamPython/tutorials/simpleFoam/pitzDaily/Allclean b/foamPython/tutorials/simpleFoam/pitzDaily/Allclean
new file mode 100755
index 0000000000000000000000000000000000000000..b25723fb17908f9ab36d8ab00225197bdf24620f
--- /dev/null
+++ b/foamPython/tutorials/simpleFoam/pitzDaily/Allclean
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# Clean times
+rm -r [1-9]* > /dev/null 2>&1 ; rm -r 0.[0-9]* > /dev/null 2>&1
+
+# Clean mesh
+rm -r constant/polyMesh
diff --git a/foamPython/tutorials/simpleFoam/pitzDaily/Allrun b/foamPython/tutorials/simpleFoam/pitzDaily/Allrun
new file mode 100755
index 0000000000000000000000000000000000000000..38f2c74e21199e24573d1badfe4a1545c0fd7add
--- /dev/null
+++ b/foamPython/tutorials/simpleFoam/pitzDaily/Allrun
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+./Allrun.pre
+
+python3 ../../../simpleFoam.py
diff --git a/foamPython/tutorials/simpleFoam/pitzDaily/Allrun.pre b/foamPython/tutorials/simpleFoam/pitzDaily/Allrun.pre
new file mode 100755
index 0000000000000000000000000000000000000000..65bbea57171fe0f0ba0155cbc6abd7db818e23a4
--- /dev/null
+++ b/foamPython/tutorials/simpleFoam/pitzDaily/Allrun.pre
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+blockMesh
diff --git a/foamPython/tutorials/simpleFoam/pitzDaily/caseSetup.py b/foamPython/tutorials/simpleFoam/pitzDaily/caseSetup.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd0d13da611f85a3262be07e7bc55b0e25a1adc3
--- /dev/null
+++ b/foamPython/tutorials/simpleFoam/pitzDaily/caseSetup.py
@@ -0,0 +1,75 @@
+# Case setup file for simpleFoam.py
+
+"""--------------------------------------------------------------------------"""
+
+# Pitz Daily
+boundaryDefinition_U = {
+'inlet':		( 'fixedValue'     		, np.array([1.0, 0.0, 0.0])), 
+'outlet':		( 'fixedGradient' 		, np.array([0.0, 0.0, 0.0])),
+'upperWall':	( 'fixedValue'   		, np.array([0.0, 0.0, 0.0])), 
+'lowerWall':	( 'fixedValue'   		, np.array([0.0, 0.0, 0.0])), 
+'frontAndBack':	( 'empty'				, None					   )
+}
+
+boundaryDefinition_p = {
+'inlet':		( 'fixedGradient' 		, np.array([0.0])),
+'outlet':		( 'fixedValue'   		, np.array([0.0])), 
+'upperWall':	( 'fixedGradient' 		, np.array([0.0])), 
+'lowerWall':	( 'fixedGradient' 		, np.array([0.0])), 
+'frontAndBack':	( 'empty'			    , None			 )
+}
+
+"""--------------------------------------------------------------------------"""
+
+internalField_U = np.array([0.0, 0.0, 0.0])
+internalField_p = np.array([0.0])
+
+# Kinematic viscosity
+nu = 0.01
+
+# Time control
+startTime = 0.0
+
+writeTime = 50
+
+deltaT = 1
+
+endTime = 50
+
+# Finite volume schemes
+
+ddtScheme = "steadyState"
+
+laplacianScheme = "linearOrthogonal"
+
+divUScheme = "upwind"
+
+gradPScheme = "linear"
+
+divHbyAScheme = "linear"
+
+# Update case setup if it is modified
+runTimeModifiable = True
+
+# fvSolution parameters
+fvSolution_U = {
+'solver'		:	'GaussSeidel',
+# 'solver'		:	'PointJacobi',
+# 'minIter'		:	0,
+# 'maxIter'		:	10,
+'tolerance'		:	1e-6,
+'relTol'		:	0.01,
+'impUR'			:	0.7
+}
+
+fvSolution_p = {
+'solver'		:	'GaussSeidel',
+# 'solver'		:	'PointJacobi',
+# 'minIter'		:	0,
+# 'maxIter'		:	100000,
+'tolerance'		:	1e-5,
+'relTol'		:	0.1,
+'expUR'			:	0.3,
+'refCell'		:	0,
+'refValue'		:	np.array([0.0])
+}
diff --git a/foamPython/tutorials/simpleFoam/pitzDaily/system/blockMeshDict b/foamPython/tutorials/simpleFoam/pitzDaily/system/blockMeshDict
new file mode 100644
index 0000000000000000000000000000000000000000..2bea353944afe08440c02bdf15e9fda7b5631518
--- /dev/null
+++ b/foamPython/tutorials/simpleFoam/pitzDaily/system/blockMeshDict
@@ -0,0 +1,153 @@
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |    
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      blockMeshDict;
+}
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+convertToMeters 0.001;
+
+vertices
+(
+    (-20.6 0 -0.5)
+    (-20.6 25.4 -0.5)
+    (0 -25.4 -0.5)
+    (0 0 -0.5)
+    (0 25.4 -0.5)
+    (206 -25.4 -0.5)
+    (206 0 -0.5)
+    (206 25.4 -0.5)
+    (290 -16.6 -0.5)
+    (290 0 -0.5)
+    (290 16.6 -0.5)
+
+    (-20.6 0 0.5)
+    (-20.6 25.4 0.5)
+    (0 -25.4 0.5)
+    (0 0 0.5)
+    (0 25.4 0.5)
+    (206 -25.4 0.5)
+    (206 0 0.5)
+    (206 25.4 0.5)
+    (290 -16.6 0.5)
+    (290 0 0.5)
+    (290 16.6 0.5)
+);
+
+negY
+(
+    (2 4 1)
+    (1 3 0.3)
+);
+
+posY
+(
+    (1 4 2)
+    (2 3 4)
+    (2 4 0.25)
+);
+
+posYR
+(
+    (2 1 1)
+    (1 1 0.25)
+);
+
+
+blocks
+(
+    hex (0 3 4 1 11 14 15 12)
+    (6 10 1)
+    simpleGrading (0.5 $posY 1)
+
+    hex (2 5 6 3 13 16 17 14)
+    (60 9 1)
+    edgeGrading (4 4 4 4 $negY 1 1 $negY 1 1 1 1)
+
+    hex (3 6 7 4 14 17 18 15)
+    (60 10 1)
+    edgeGrading (4 4 4 4 $posY $posYR $posYR $posY 1 1 1 1)
+
+    hex (5 8 9 6 16 19 20 17)
+    (8 9 1)
+    simpleGrading (2.5 1 1)
+
+    hex (6 9 10 7 17 20 21 18)
+    (8 10 1)
+    simpleGrading (2.5 $posYR 1)
+);
+
+edges
+(
+);
+
+boundary
+(
+    inlet
+    {
+        type patch;
+        faces
+        (
+            (0 1 12 11)
+        );
+    }
+    outlet
+    {
+        type patch;
+        faces
+        (
+            (8 9 20 19)
+            (9 10 21 20)
+        );
+    }
+    upperWall
+    {
+        type wall;
+        faces
+        (
+            (1 4 15 12)
+            (4 7 18 15)
+            (7 10 21 18)
+        );
+    }
+    lowerWall
+    {
+        type wall;
+        faces
+        (
+            (0 3 14 11)
+            (3 2 13 14)
+            (2 5 16 13)
+            (5 8 19 16)
+        );
+    }
+    frontAndBack
+    {
+        type empty;
+        faces
+        (
+            (0 3 4 1)
+            (2 5 6 3)
+            (3 6 7 4)
+            (5 8 9 6)
+            (6 9 10 7)
+            (11 14 15 12)
+            (13 16 17 14)
+            (14 17 18 15)
+            (16 19 20 17)
+            (17 20 21 18)
+        );
+    }
+);
+
+// ************************************************************************* //
diff --git a/foamPython/tutorials/simpleFoam/pitzDaily/system/controlDict b/foamPython/tutorials/simpleFoam/pitzDaily/system/controlDict
new file mode 100644
index 0000000000000000000000000000000000000000..a0ecc3f9bace177d3d7288fad4407dbd68a0aa33
--- /dev/null
+++ b/foamPython/tutorials/simpleFoam/pitzDaily/system/controlDict
@@ -0,0 +1,26 @@
+/*---------------------------------------------------------------------------*\
+ ####                 ####     #   #             |
+ #                    #  #     ##  #             | FoamPython
+ ##  #### ####  ##### #### # # #   #### #### ### | v1.0
+ #   #  # #  #  # # # #     #  # # #  # #  # # # |
+ #   #### ##### # # # #    #    #  #  # #### # # |    
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    object      controlDict;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// NOTE: controlDict is needed for blockMesh or other utilities
+
+application     xyz;
+
+deltaT          123;
+
+writeInterval   123;
+
+
+// ************************************************************************* //