Field Transfer Threads Performace

Import

[1]:
from Muscat.Containers.MeshFieldOperations import GetFieldTransferOpCpp, GetFieldTransferOpPython
from Muscat.Containers.MeshCreationTools import CreateCube
from Muscat.FE.FETools import PrepareFEComputation
from Muscat.FE.Fields.FEField import FEField
from Muscat.Containers.ConstantRectilinearMeshTools import CreateConstantRectilinearMesh
from Muscat.LinAlg.Transform import Transform
from Muscat.Helpers.TextFormatHelper import TFormat
from Muscat.Containers.NativeTransfer import NativeTransfer
import numpy as np
import time
import pyvista
pyvista.global_theme._jupyter_backend = 'panel' # remove this line to get interactive 3D plots

Mesh and Target Points Generation

[2]:
N = 50
inputmesh = CreateCube(dimensions=[N, N, N], origin=[-1.0] * 3, spacing=[2 / (N - 1), 2 / (N - 1), 2 / (N - 1)])
inputFEField = FEField(name="3DTo3DNative", mesh=inputmesh)

N = 60
outmesh = CreateConstantRectilinearMesh(dimensions=[N, N, N], origin=[-1.0] * 3, spacing=[2 / (N - 1), 2 / (N - 1), 2 / (N - 1)])
print("Input mesh:")
print(inputmesh)
print("Output mesh:")
print(outmesh)
Input mesh:
Mesh
  Number Of Nodes    : 125000
    Tags : (x0y0z0:1) (x1y0z0:1) (x0y1z0:1) (x1y1z0:1) (x0y0z1:1) (x1y0z1:1) (x0y1z1:1) (x1y1z1:1)
  Number Of Elements : 132055
    StructuredElementContainer([50 50 50]),   Type : (ElementType.Hexahedron_8,117649),   Tags : (3D:117649)
    ElementsContainer,   Type : (ElementType.Quadrangle_4,14406),   Tags : (Skin:14406) (X0:2401) (X1:2401) (Y0:2401) (Y1:2401) (Z0:2401) (Z1:2401)

Output mesh:
Mesh
  Number Of Nodes    : 216000
    Tags :
  Number Of Elements : 205379
    StructuredElementContainer([60 60 60]),   Type : (ElementType.Hexahedron_8,205379),   Tags :

Deform output mesh

Make the problem more realistic

[3]:
op = Transform()
op.keepNormalized = False
op.keepOrthogonal = False
op.SetFirst([1.4, 0.56, 0])
op.SetSecond([-1.135, 1.42486, 1.6102])

outmesh.nodes = np.ascontiguousarray(op.ApplyTransform(outmesh.nodes))

Generate Data

[4]:
x = inputmesh.nodes[:, 0]
y = inputmesh.nodes[:, 1]
data = (x - 0.5) ** 2 - y * 0.5 + x * y * 0.25

Start transfer timing…

[5]:
print(f"Number of elements in the origin mesh = {inputmesh.GetNumberOfElements()}")
print(f"Number of points in the target cloud point = {outmesh.GetNumberOfNodes()}")
Number of elements in the origin mesh = 132055
Number of points in the target cloud point = 216000

Some utilities

[6]:
def PrintRow(datarow):
    res = "|"
    for d in datarow:
        if type(d) is str:
            res += TFormat.Center(str(d), fill=" ", width=20) + "|"
        else:
            res += TFormat.Center("%.4f" % d, fill=" ", width=20) + "|"
    print(res)

Initialization

[7]:
setFieldTime = time.time()
nt = NativeTransfer()
nt.SetVerbose(False)
searchStrategies = ((True,False,False,False),
                 (False,True,False,False),
                  (False,True,True,False),
                  (False,False,False,True),
                  (True,True,False,True))

for ss in searchStrategies:
    nt.SetUsePointSearch(ss[0])
    nt.SetUseElementSearch(ss[1])
    nt.SetUseElementSearchFast(ss[2])
    nt.SetUseEdgeSearch(ss[3])
    setFieldTime = time.time()
    nt.SetSourceFEField(inputFEField)
    st = time.time() - setFieldTime
    print(f"Set Up time (SetSourceFEField) for {ss} : {st} [s]")
Set Up time (SetSourceFEField) for (True, False, False, False) : 0.11120080947875977 [s]
Set Up time (SetSourceFEField) for (False, True, False, False) : 0.09433293342590332 [s]
Set Up time (SetSourceFEField) for (False, True, True, False) : 0.09303522109985352 [s]
Set Up time (SetSourceFEField) for (False, False, False, True) : 0.2518117427825928 [s]
Set Up time (SetSourceFEField) for (True, True, False, True) : 0.25812458992004395 [s]

Threads Scalability

[8]:
nt.SetUsePointSearch(True)
nt.SetUseElementSearch(False)
nt.SetUseElementSearchFast(False)
nt.SetUseEdgeSearch(False)
nt.SetSourceFEField(inputFEField)
[9]:
from Muscat.Helpers.CPU import GetNumberOfAvailableCores
print(f"GetNumberOfAvailableCores: {GetNumberOfAvailableCores()}")
nbCores = (np.arange(int(np.ceil(np.sqrt(GetNumberOfAvailableCores()))))**2+1)
nbCores[-1] = GetNumberOfAvailableCores()
GetNumberOfAvailableCores: 1
[10]:
print(f"Set Up time (SetSourceFEField) {st} [s]")
print("_"*(8*20+9))
output = ["method"]
output.extend(f"cpp [s] {th} thread " for th in nbCores)
output.extend(f"speedup {th}/1  " for th in nbCores[1:])
PrintRow(output)
for method in ["Interp/Nearest", "Nearest/Nearest", "Interp/Clamp", "Interp/Extrap"]:
    nt.SetTransferMethod(method)
    nt.SetTargetPoints(outmesh.nodes)
    times = []
    for nbThreads in nbCores:
        nt.SetMaxConcurrency(nbThreads)
        cppStartTime = time.time()
        nt.Compute()
        cppStopTime = time.time()
        op = nt.GetOperator()
        times.append(cppStopTime - cppStartTime)

    output = [method]
    output.extend(times)
    output.extend([times[0] / (t if t > 0 else 1) for t in times[1:]])

    PrintRow(output)
Set Up time (SetSourceFEField) 0.25812458992004395 [s]
_________________________________________________________________________________________________________________________________________________________________________
|       method       |  cpp [s] 1 thread  |
|   Interp/Nearest   |       2.9964       |
|   Nearest/Nearest  |       0.4111       |
|    Interp/Clamp    |       3.0013       |
|    Interp/Extrap   |       3.0070       |