if (('PFVFLOW' in features) and ('TWOPHASEFLOW' in features)):
	from yade import pack
	from yade import export
	from yade import timing
	from yade import plot
	import time
	from math import *

	num_spheres = 1000  # number of spheres
	young = 1e6
	compFricDegree = 3  # initial contact friction during the confining phase
	finalFricDegree = 30  # contact friction during the deviatoric loading
	mn, mx = Vector3(0, 0, 0), Vector3(1, 1, 0.4)  # corners of the initial packing
	graindensity = 2600
	toleranceWarning = 1.e-11
	toleranceCritical = 1.e-6

	O.materials.append(FrictMat(young=young, poisson=0.5, frictionAngle=radians(compFricDegree), density=graindensity, 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 = pack.SpherePack()
	sp.makeCloud(mn, mx, -1, 0.3333, num_spheres, False, 0.95, seed=1)  #"seed" make the "random" generation always the same
	sp.toSimulation(material='spheres')

	triax = TriaxialStressController(
	        maxMultiplier=1. + 2e4 / young,  # spheres growing factor (fast growth)
	        finalMaxMultiplier=1. + 2e3 / young,  # spheres growing factor (slow growth)
	        thickness=0,
	        stressMask=7,
	        max_vel=0.005,
	        internalCompaction=True,  # If true the confining pressure is generated by growing particles
	)

	newton = NewtonIntegrator(damping=0.2)

	O.engines = [
	        ForceResetter(),
	        InsertionSortCollider([Bo1_Sphere_Aabb(), Bo1_Box_Aabb()]),
	        InteractionLoop(
	                [Ig2_Sphere_Sphere_ScGeom(), Ig2_Box_Sphere_ScGeom()], [Ip2_FrictMat_FrictMat_FrictPhys()], [Law2_ScGeom_FrictPhys_CundallStrack()],
	                label="iloop"
	        ),
	        TwoPhaseFlowEngine(dead=1, label="flow"),  #introduced as a dead engine for the moment, see 2nd section
	        GlobalStiffnessTimeStepper(active=1, timeStepUpdateInterval=100, timestepSafetyCoefficient=0.8),
	        triax,
	        newton
	]

	triax.goal1 = triax.goal2 = triax.goal3 = -10000

	while 1:
		O.run(1000, True)
		unb = unbalancedForce()
		if unb < 0.001 and abs(-10000 - triax.meanStress) / 10000 < 0.001:
			break

	setContactFriction(radians(finalFricDegree))

	radius = 0
	for b in O.bodies:
		if b.state.mass == 0:
			b.state.blockedDOFs = 'xyzXYZ'
			b.state.vel = (0, 0, 0)
			b.state.angVel = (0, 0, 0)
		if b.state.mass > 0:
			radius += b.shape.radius
			#b.state.blockedDOFs='xyz'
			#b.state.vel=(0,0,0)
	radius = radius / num_spheres

	triax.dead = True
	while 1:
		O.run(1000, True)
		unb = unbalancedForce()
		if unb < 0.001:
			break

	press = 1000.
	O.run(10, 1)
	flow.dead = 0
	flow.meshUpdateInterval = -1
	flow.useSolver = 3
	flow.permeabilityFactor = 1
	flow.viscosity = 0.1

	flow.bndCondIsWaterReservoir = [0, 0, 1, 0, 0, 0]

	flow.bndCondIsPressure = [0, 0, 1, 0, 0, 0]
	flow.bndCondValue = [0, 0, press, 0, 0, 0]
	flow.boundaryUseMaxMin = [0, 0, 0, 0, 0, 0]
	flow.iniVoidVolumes = True
	newton.damping = 0.1
	GlobalStiffnessTimeStepper.dead = True
	O.dt = min(0.8 * PWaveTimeStep(), 0.8 * 1. / 1200. * pi / flow.viscosity * graindensity * radius**2)
	O.dynDt = False

	flow.surfaceTension = 0.0
	flow.drainageFirst = False
	flow.isDrainageActivated = False
	flow.isImbibitionActivated = True
	flow.isCellLabelActivated = True
	flow.initialization()
	cs = flow.getClusters()
	c0 = cs[1]

	voidvol = 0.0
	voidvoltot = 0.0
	nvoids = flow.nCells()
	initialvol = [0] * (nvoids)
	bar = [0] * (nvoids)
	initiallevel = O.bodies[flow.wallIds[flow.ymin]
	                       ].state.pos[1] + (O.bodies[flow.wallIds[flow.ymax]].state.pos[1] - O.bodies[flow.wallIds[flow.ymin]].state.pos[1]) / 3

	for ii in range(nvoids):
		initialvol[ii] = 1. / flow.getCellInvVoidVolume(ii)
		voidvoltot += initialvol[ii]
		bar[ii] = flow.getCellBarycenter(ii)[1]

	iniok = 0
	while (iniok == 0):
		celleini1 = [nvoids + 1] * (nvoids)
		celleini0 = [0] * (nvoids)
		for ii in range(len(c0.getInterfaces())):
			if bar[c0.getInterfaces()[ii][1]] < initiallevel:
				if celleini1[c0.getInterfaces()[ii][1]] == nvoids + 1:
					celleini1[c0.getInterfaces()[ii][1]] = ii
					celleini0[c0.getInterfaces()[ii][1]] = c0.getInterfaces()[ii][0]
		for ii in range(nvoids):
			if celleini1[ii] != nvoids + 1:
				flow.clusterOutvadePore(celleini0[ii], ii)
		no = 0
		for ii in range(nvoids):
			if bar[ii] < initiallevel:
				if flow.getCellLabel(ii) == 0:
					no = 1
		if no == 0:
			iniok = 1
			for ii in range(len(c0.getInterfaces())):
				c0.setCapVol(ii, 0.0)

	c0.solvePressure()
	flow.computeCapillaryForce()
	for b in O.bodies:
		O.forces.setPermF(b.id, flow.fluidForce(b.id))
	O.run(1, 1)
	flow.savePhaseVtk("./vtk", True)

	timeini = O.time
	ini = O.iter

	Qin = 0.0
	#Qout=0.0

	totalflux = [0] * (nvoids)
	#totalCellSat=0.0

	for ii in range(nvoids):
		if flow.getCellLabel(ii) == 0:
			voidvol += initialvol[ii]

	bubble = 0
	dd = 0.0
	celleok = [0] * (nvoids)
	deltabubble = 0
	col0 = [0] * (nvoids)
	neighK = [0.0
	         ] * (nvoids)  #FIXME: after remeshing the size will be invalid since nvoids can change, initializations will have to go in the function itself

	def pressureImbibition():
		global Qin, total2, dd, deltabubble, bubble

		c0.updateCapVolList(O.dt)

		Qin += -1. * (flow.getBoundaryFlux(flow.wallIds[flow.ymin])) * O.dt
		#Qout+=(flow.getBoundaryFlux(flow.wallIds[flow.ymax]))*O.dt

		col1 = [0] * (nvoids)
		delta = [0.0] * (nvoids)
		for ii in range(nvoids):
			if flow.getCellLabel(ii) == 0:
				totalflux[ii] += -1. * flow.getCellFluxFromId(ii) * O.dt
				if (totalflux[ii]) >= initialvol[ii]:
					col1[ii] = 1
				if (totalflux[ii]) > initialvol[ii]:
					delta[ii] = totalflux[ii] - initialvol[ii]
					totalflux[ii] += -1 * delta[ii]
					#dd+=delta[ii]

		# advices:
		# never write 'getInterfaces()' inside a loop, it's expensive, get the list once outside loop
		# get interfaces again only if you know the list could have change (cluster got/lost pores).
		# I'm fixing only the first loop below (old version left commented)
		#
		for ii in range(len(c0.getInterfaces())):
			ll = c0.getInterfaces()[ii][1]
			if col1[ll] == 1:
				if celleok[ll] == 0:
					celleok[ll] = 1
					col0[ll] = c0.getInterfaces()[ii][0]

		for jj in range(nvoids):
			if col1[jj] == 1:
				flow.clusterOutvadePore(col0[jj], jj)
				#totalCellSat+=initialvol[jj]

		for ii in range(len(c0.getInterfaces())):
			ll = c0.getInterfaces()[ii][0]
			if delta[ll] != 0:
				neighK[ll] += c0.getConductivity(ii)
		for ii in range(len(c0.getInterfaces())):
			ll = c0.getInterfaces()[ii][0]
			if delta[ll] != 0:
				c0.setCapVol(ii, delta[ll] / neighK[ll] * c0.getConductivity(ii))
				totalflux[c0.getInterfaces()[ii][1]] += delta[ll] / neighK[ll] * c0.getConductivity(ii)
		for ii in range(nvoids):
			if delta[ii] != 0:
				if neighK[ii] == 0:
					deltabubble += delta[ii]
					bubble += 1

		col1 = [0] * (nvoids)
		delta = [0.0] * (nvoids)
		for ii in range(nvoids):
			if flow.getCellLabel(ii) == 0:
				if (totalflux[ii]) >= initialvol[ii]:
					col1[ii] = 1
				if (totalflux[ii]) > initialvol[ii]:
					delta[ii] = totalflux[ii] - initialvol[ii]
					totalflux[ii] += -1 * delta[ii]
					#dd+=delta[ii]
		if col1 != [0] * (nvoids):
			for ii in range(len(c0.getInterfaces())):
				ll = c0.getInterfaces()[ii][1]
				if col1[ll] == 1:
					if celleok[ll] == 0:
						celleok[ll] = 1
						col0[ll] = c0.getInterfaces()[ii][0]
			for jj in range(nvoids):
				if col1[jj] == 1:
					flow.clusterOutvadePore(col0[jj], jj)
					#totalCellSat+=initialvol[jj]
			for ii in range(len(c0.getInterfaces())):
				ll = c0.getInterfaces()[ii][0]
				if delta[ll] != 0:
					neighK[ll] += c0.getConductivity(ii)
			for ii in range(len(c0.getInterfaces())):
				ll = c0.getInterfaces()[ii][0]
				if delta[ll] != 0:
					c0.setCapVol(ii, delta[ll] / neighK[ll] * c0.getConductivity(ii))
					totalflux[c0.getInterfaces()[ii][1]] += delta[ll] / neighK[ll] * c0.getConductivity(ii)
			for ii in range(nvoids):
				if delta[ii] != 0:
					if neighK[ii] == 0:
						deltabubble += delta[ii]
						bubble += 1
			col1 = [0] * (nvoids)
			delta = [0.0] * (nvoids)
			for ii in range(nvoids):
				if flow.getCellLabel(ii) == 0:
					if (totalflux[ii]) >= initialvol[ii]:
						col1[ii] = 1
					if (totalflux[ii]) > initialvol[ii]:
						delta[ii] = totalflux[ii] - initialvol[ii]
						totalflux[ii] += -1 * delta[ii]
						dd += delta[ii]
						print(O.iter, 'waterloss', ii, delta[ii])
			if col1 != [0] * (nvoids):
				for ii in range(len(c0.getInterfaces())):
					ll = c0.getInterfaces()[ii][1]
					if col1[ll] == 1:
						if celleok[ll] == 0:
							celleok[ll] = 1
							col0[ll] = c0.getInterfaces()[ii][0]
				for jj in range(nvoids):
					if col1[jj] == 1:
						flow.clusterOutvadePore(col0[jj], jj)
						#totalCellSat+=initialvol[jj]

		total2 = 0.0
		for ii in range(nvoids):
			total2 += totalflux[ii]

		c0.solvePressure()
		flow.computeCapillaryForce()
		for b in O.bodies:
			O.forces.setPermF(b.id, flow.fluidForce(b.id))

	file = open('Test.txt', "w")
	checkdifference = 0

	def equilibriumtest():
		global F33, F22, checkdifference
		#unbalanced=utils.unbalancedForce()
		F33 = abs(O.forces.f(flow.wallIds[flow.ymax])[1])
		F22 = abs(O.forces.f(flow.wallIds[flow.ymin])[1])
		#F11 =abs(O.forces.f(flow.wallIds[flow.xmax])[0]),
		#F00=abs(O.forces.f(flow.wallIds[flow.xmin])[0]),
		#F44=abs(O.forces.f(flow.wallIds[flow.zmin])[2]),
		#F55=abs(O.forces.f(flow.wallIds[flow.zmax])[2]),
		deltaF = abs(F33 - F22)
		file.write(str(O.iter) + " " + str(F33) + " " + str(F22) + " " + str(deltaF) + "\n")
		if O.time >= timeini + 1.5:
			if checkdifference == 0:
				print('check F done')
				if deltaF > 0.01 * press:
					raise YadeCheckError('Error: too high difference between forces acting at the bottom and upper walls')
					#O.pause()
				checkdifference = 1

	once = 0

	def fluxtest():
		global once, QinOk
		no = 0

		QinOk = Qin - deltabubble
		error = QinOk - total2
		if error > toleranceWarning:
			print(
			        "Warning: difference between total water volume flowing through bottom wall and water loss due to air bubble generations",
			        QinOk, " vs. total water volume flowing inside dry or partially saturated cells", total2
			)
		if error > toleranceCritical:
			raise YadeCheckError("The difference is more, than the critical tolerance!")
		file.write(str(O.time - timeini) + " " + str(total2) + " " + str(QinOk) + " " + str(error) + "\n")

		for ii in range(nvoids):
			if flow.getCellLabel(ii) == 0:
				no = 1
		if once == 0:
			if no == 0:
				imbtime = O.time - timeini
				print(imbtime, voidvol, total2, QinOk)
				if voidvol - total2 > toleranceWarning:
					print(
					        "Warning: initial volume of dry voids", voidvol,
					        " vs. total water volume flowing inside dry or partially saturated cells", total2
					)
				if voidvol - total2 > toleranceCritical:
					raise YadeCheckError("The difference is more, than the critical tolerance!")
				file.write(str(imbtime) + " " + str(voidvol) + " " + str(total2) + " " + str(QinOk) + "\n")
				once = 1
				timing.stats()

	def addPlotData():
		plot.addData(i1=O.iter, t=O.time, Fupper=F33, Fbottom=F22, Q=QinOk, T=total2)

	plot.live = True
	plot.plots = {' t ': ('Fupper', 'Fbottom'), 't': ('Q', 'T')}
	plot.plot()

	def pl():
		flow.savePhaseVtk("./vtk", True)

	O.engines = O.engines + [PyRunner(iterPeriod=100, command='pl()')]
	#O.engines=O.engines+[VTKRecorder(iterPeriod=100,recorders=['spheres'],fileName='./exp')]
	O.engines = O.engines + [PyRunner(iterPeriod=1, command='equilibriumtest()')]
	O.engines = O.engines + [PyRunner(iterPeriod=1, command='pressureImbibition()')]
	O.engines = O.engines + [PyRunner(iterPeriod=1, command='fluxtest()')]
	O.engines = O.engines + [PyRunner(iterPeriod=1, command='addPlotData()')]

	O.timingEnabled = True
	#file.close()
	#plot.saveDataTxt('plots.txt',vars=('i1','t','Fupper','Fbottom','Q','T'))

	#O.run(1,1)

	import tempfile, shutil
	dirpath = tempfile.mkdtemp()
	for fileName in ['./vtk', './Test.txt']:
		if (os.path.exists(fileName)):
			shutil.move(fileName, dirpath)
		print("File %s moved into %s/ directory" % (fileName, dirpath))

else:
	print("This check_TwoPhaseFlowEngine_PressureInjection.py cannot be executed because TWOPHASEFLOW or PFVFLOW are disabled")
