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; + + +// ************************************************************************* //