Advection stops working in ThermalEngine when decoupleForces in FlowEngine is True

Asked by Zoheir Khademian

Hello

I took the example flowScenario.py [1] and turned decoupleForces [2] true to avoid applying viscus and pressure forces on particles. My goal is to speed up the simulation as I am not interested in mechanical responses. However, it seems fluid conduction and advection stop working when this option is on. This is the only change I made to [1] but put the MWE here as well.
My question is if there is a way to get fluid conduction and advection work without having the flow engine calculate the forces on the particles, assuming I am not missing anything here.

Thanks

Zoheir

[1] https://gitlab.com/yade-dev/trunk/-/blob/master/examples/ThermalEngine/flowScenario.py
[2] https://yade-dem.org/doc/yade.wrapper.html#yade.wrapper.FlowEngine.decoupleForces

from yade import pack, ymport
from yade import timing
import numpy as np
import shutil
timeStr = time.strftime('%m-%d-%Y')
num_spheres=1000# number of spheres
young=1e9
rad=0.003

mn,mx=Vector3(0,0,0),Vector3(0.05,0.05,0.05) # corners of the initial packing

thermalCond = 2. #W/(mK)
heatCap = 710. #J(kg K)
t0 = 333.15 #K

# micro properties
r = rad
k = 2.0 # 2*k*r
Cp = 710.
rho = 2600.
D = 2.*r
m = 4./3.*np.pi*r**2/rho
# macro diffusivity

identifier = '-flowScenario'

if not os.path.exists('VTK'+timeStr+identifier):
 os.mkdir('VTK'+timeStr+identifier)
else:
 shutil.rmtree('VTK'+timeStr+identifier)
 os.mkdir('VTK'+timeStr+identifier)

if not os.path.exists('txt'+timeStr+identifier):
 os.mkdir('txt'+timeStr+identifier)
else:
 shutil.rmtree('txt'+timeStr+identifier)
 os.mkdir('txt'+timeStr+identifier)

shutil.copyfile(sys.argv[0],'txt'+timeStr+identifier+'/'+sys.argv[0])

O.materials.append(FrictMat(young=young,poisson=0.5,frictionAngle=radians(3),density=2600,label='spheres'))
O.materials.append(FrictMat(young=young,poisson=0.5,frictionAngle=0,density=0,label='walls'))
walls=aabbWalls([mn,mx],thickness=0,material='walls')
wallIds=O.bodies.append(walls)

sp = O.bodies.append(ymport.textExt('5cmEdge_1mm.spheres', 'x_y_z_r',color=(0.1,0.1,0.9), material='spheres'))

print('num bodies ', len(O.bodies))

triax=TriaxialStressController(
 maxMultiplier=1.+2e4/young,
 finalMaxMultiplier=1.+2e3/young,
 thickness = 0,
 stressMask = 7,
 internalCompaction=True,
)

ThermalEngine = ThermalEngine(dead=1,label='thermal');

newton=NewtonIntegrator(damping=0.2)
intRadius = 1
O.engines=[
 ForceResetter(),
 InsertionSortCollider([Bo1_Sphere_Aabb(aabbEnlargeFactor=intRadius),Bo1_Box_Aabb()]),
 InteractionLoop(
  [Ig2_Sphere_Sphere_ScGeom(interactionDetectionFactor=intRadius),Ig2_Box_Sphere_ScGeom()],
  [Ip2_FrictMat_FrictMat_FrictPhys()],
  [Law2_ScGeom_FrictPhys_CundallStrack()],label="iloop"
 ),
 FlowEngine(dead=1,label="flow",multithread=False),
 ThermalEngine, GlobalStiffnessTimeStepper(active=1,timeStepUpdateInterval=100,timestepSafetyCoefficient=0.8),
 triax,
 VTKRecorder(iterPeriod=500,fileName='VTK'+timeStr+identifier+'/spheres-',recorders=['spheres','thermal','intr'],dead=1,label='VTKrec'),
 newton
]

#goal = -1e5
#triax.goal1=triax.goal2=triax.goal3=goal

for b in O.bodies:
 if isinstance(b.shape, Sphere):
  b.dynamic=False # mechanically static

flow.dead=0
flow.defTolerance=-1 #0.3
flow.meshUpdateInterval=-1
flow.useSolver=4
flow.permeabilityFactor= 1
flow.viscosity= 0.001
flow.bndCondIsPressure=[1,1,0,0,0,0]
flow.bndCondValue=[10,0,0,0,0,0]
flow.thermalEngine=True
flow.debug=False
flow.fluidRho = 997
flow.fluidCp = 4181.7
flow.getCHOLMODPerfTimings=True
flow.bndCondIsTemperature=[1,0,0,0,0,0]
flow.thermalEngine=True
flow.thermalBndCondValue=[343.15,0,0,0,0,0]
flow.tZero=t0
flow.pZero=0
flow.maxKdivKmean=1
flow.minKdivmean=0.0001;
flow.decoupleForces = True

thermal.dead=0
thermal.debug=False
thermal.fluidConduction=True
thermal.ignoreFictiousConduction=True
thermal.conduction=True
thermal.thermoMech=False
thermal.solidThermoMech = False
thermal.fluidThermoMech = False
thermal.advection=True
thermal.bndCondIsTemperature=[0,0,0,0,0,0]
thermal.thermalBndCondValue=[0,0,0,0,0,0]
thermal.fluidK = 0.6069 #0.650
thermal.fluidConductionAreaFactor=1.
thermal.particleT0 = t0
thermal.particleDensity=2600.
thermal.particleK = thermalCond
thermal.particleCp = heatCap
thermal.useKernMethod=True
#thermal.useHertzMethod=False
timing.reset()

O.dt=0.1e-3
O.dynDt=False

O.run(1,1)
flow.dead=0

def bodyByPos(x,y,z):
 cBody = O.bodies[1]
 cDist = Vector3(100,100,100)
 for b in O.bodies:
  if isinstance(b.shape, Sphere):
   dist = b.state.pos - Vector3(x,y,z)
   if np.linalg.norm(dist) < np.linalg.norm(cDist):
    cDist = dist
    cBody = b
 print('found closest body ', cBody.id, ' at ', cBody.state.pos)
 return cBody

#bodyOfInterest = bodyByPos(15.998e-3,0.0230911,19.5934e-3)
bodyOfInterest = bodyByPos(0.025,0.025,0.025)

# find 10 bodies along x axis
axis = np.linspace(mn[0], mx[0], num=5)
axisBodies = [None] * len(axis)
axisTrue = np.zeros(len(axis))
for i,x in enumerate(axis):
 axisBodies[i] = bodyByPos(x, mx[1]/2, mx[2]/2)
 axisTrue[i] = axisBodies[i].state.pos[0]

print("found body of interest at", bodyOfInterest.state.pos)

from yade import plot

## a function saving variables
def history():
 plot.addData(
  ftemp1=flow.getPoreTemperature((0.024,0.023,0.02545)),
  p=flow.getPorePressure((0.025,0.025,0.025)),
  t=O.time,
  i = O.iter,
  temp1 = axisBodies[0].state.temp,
  temp2 = axisBodies[1].state.temp,
  temp3 = axisBodies[2].state.temp,
  temp4 = axisBodies[3].state.temp,
  temp5 = axisBodies[4].state.temp,
  bodyOfIntTemp = O.bodies[bodyOfInterest.id].state.temp
)
 plot.saveDataTxt('txt'+timeStr+identifier+'/temps'+identifier+'.txt',vars=('t','i','p','ftemp1', 'temp1','temp2','temp3','bodyOfIntTemp'))

O.engines=O.engines+[PyRunner(iterPeriod=500,command='history()',label='recorder')]

def pressureField():
 flow.saveVtk('VTK'+timeStr+identifier+'/',withBoundaries=False)
O.engines=O.engines+[PyRunner(iterPeriod=2000,command='pressureField()')]

def endFlux():
 if O.time >= 30:
  flux = 0
  n=utils.porosity()
  for i in flow.getBoundaryVel(1):
   flux +=i[0]*i[3]/n # area * velocity / porosity (dividing by porosity because flow engine is computing the darcy velocity)
  massFlux = flux * 997

  K = abs(flow.getBoundaryFlux(1))*(flow.viscosity*0.5)/(0.5**2.*(10.-0))
  d=8e-3 # sphere diameter
  Kc = d**2/180. * (n**3.)/(1.-n)**2

  print('Permeability', K, 'kozeny', Kc)
  print('outlet flux(with vels only):',massFlux,'compared to CFD = 0.004724 kg/s')
  print('sim paused')
  O.pause()

O.engines=O.engines+[PyRunner(iterPeriod=10,command='endFlux()')]
VTKrec.dead=0
from yade import plot

plot.plots={'t':(('ftemp1','k-'),('bodyOfIntTemp','r-'))} #
plot.plot()
O.saveTmp()

print("starting thermal sim")
O.run()

Question information

Language:
English Edit question
Status:
Solved
For:
Yade Edit question
Assignee:
No assignee Edit question
Solved by:
Robert Caulk
Solved:
Last query:
Last reply:
Revision history for this message
Robert Caulk (rcaulk) said :
#1

Hey Zoheir,

Thanks for another good question.

>> My question is if there is a way to get fluid conduction and advection work without having the flow engine calculate the forces on the particles, assuming I am not missing anything here.

We can surely sort it out, if it isn't working at the moment.

To start, do you need any body movement? Or can you simply set all O.bodies[i].dynamic=False?

Does decoupleForces work on the standard oedometer.py example script [1]? When you deactivate advection, does it work?

I will try to make some time to dive into it and get back to you.

-rc

[1]https://gitlab.com/yade-dev/trunk/-/blob/master/examples/FluidCouplingPFV/oedometer.py

Revision history for this message
Zoheir Khademian (zkhademian) said :
#2

Hi Robert and thanks!

>>To start, do you need any body movement? Or can you simply set all O.bodies[i].dynamic=False?

I dont need any movement so I set all body dynamic to false but still the viscus and pressure forces are calculated in the FlowEngine, which adds to the runtime. To avoid this calculations, I tried setting decoupleForces=True but advection and fluid convection stopped working. I think the calculation of cell temperatures is somehow dependent on the decoupleForces option.

>>Does decoupleForces work on the standard oedometer.py example script [1]?

Yes, in the oedometer.py example script, I set decoupleForces true and it prevented the viscus and pressure force calculations. The engine time table that is printed in the terminal showed that time spent on force calculation is zero. From the plots when running the example, the vertical strain was not affected by the pore pressure changes during the test.
So, I think decoupleForces works fine but advection and fluid conduction does not if decoupleForces is true.

Another question is where cell temperature is updated in the source code? I think cell->info().temp() is not updated when decoupleForces is true.

Thanks
Zoheir

Revision history for this message
Best Robert Caulk (rcaulk) said :
#3

Hey Zoheir,

This "flow.decoupleForces" was not designed to be used directly in this manner. It is meant to be controlled by ThermalEngine with thermal.letThermalRunFlowForceupdates. The problem is that thermal.letThermalRunFlowForce does not give you the functionality you desire because it still computes and applies the forces. I probably shouldn't have exposed flow.decoupleForces to the user in this way. But it is ok, your use case makes sense. So it is just a matter of fixing it so that decoupleForces can be used in this way.

After diving in, nothing is actually broken for advection, it is running normally behind the scenes. The issue is that the "solver->noCache" flag is well embedded into various FlowEngine functionalities, such as the getter functions you use (getPoreTemperature()). When you use flow.decoupleForces directly, the noCache flag is never set properly, and hinders the use of the getters. Here is a merge request which fixes your problem and allows no-remesh sims to still use getters [3]. Please confirm if it works for you or not.

>>Another question is where cell temperature is updated in the source code?

https://gitlab.com/yade-dev/trunk/-/blob/master/lib/triangulation/FlowBoundingSphereLinSolv.ipp#L826

>>I think cell->info().temp() is not updated when decoupleForces is true.

Can you link to where in the source code you see this?

Cheers,

Robert

[1]https://gitlab.com/yade-dev/trunk/-/blob/master/pkg/pfv/FlowEngine.ipp.in#L105
[2]https://gitlab.com/yade-dev/trunk/-/blob/master/lib/triangulation/FlowBoundingSphere.hpp#L255
[3]https://gitlab.com/yade-dev/trunk/-/merge_requests/799

Revision history for this message
Zoheir Khademian (zkhademian) said :
#4

Hi Robert,
Thanks for very nice explanation.

>> Please confirm if it works for you or not.

Yes, decoupleForces works well now.

Thank you!

Zoheir

Revision history for this message
Zoheir Khademian (zkhademian) said :
#5

Thanks Robert Caulk, that solved my question.