<commit_before>function Liquid(attrs) {
	//driving force convention: positive -> into liquid, neg -> into vapor
	//still need to do pInt adjustment for wallGas
	this.type = 'Liquid';
	this.wallInfo = attrs.wallInfo;
	this.wallGas = walls[this.wallInfo];
	this.wallPtIdxs = attrs.wallPts || [this.wallGas.length - 2, this.wallGas.length - 3];
	this.handle = attrs.handle;
	this.cleanUpWith = attrs.cleanUpWith || currentSetupType;
	this.actCoeffType = attrs.actCoeffType; //twoSfxMrg for two suffix margules, 
	this.actCoeffInfo = attrs.actCoeffInfo;
	this.Cp = 0; //to be set each turn/absorption and used in setting temperatures for reflecting dots.  Capital C denotes total heat capacity.
	var tempInit = attrs.tempInit;
	this.temp = tempInit;
	this.spcInfo = this.makeSpcInfo(attrs.spcInfo); //formatted as {spc1: {count: #, spcVol: #, cP: #, antoineCoeffs: {a: #,b: #,c: #}, hVap: #} ... } spcVol in L/mol, hVap in kj/mol
	this.drivingForce = this.makeDrivingForce(this.spcInfo);
	this.dotMgrLiq = this.makeDotManager(this.spcInfo);
	this.wallLiq = this.makeWallLiq(this.spcInfo, this.wallGas, this.wallPtIdxs, this.dotMgrLiq);
	this.numAbs = this.makeNumAbsorbed(this.spcInfo);
	this.numEjt = deepCopy(this.numAbs);
	this.makeDots(this.wallLiq, this.wallGas, this.wallPtIdxs, this.spcInfo, tempInit, this.dotMgrLiq) && this.deleteCount(this.spcInfo);
	this.dataGas = this.initData(this.wallGas, this.spcInfo, ['pInt', 'temp']);
	this.dataLiq = this.initData(this.wallLiq, this.spcInfo);
	this.drawList = this.makeDrawList(this.dotMgrLiq); //need to make draw list in random order otherwise dots drawn on top will look more prominant than they are.
	this.actCoeffFuncs = this.makeActCoeffFuncs(this.actCoeffType, this.actCoeffInfo, this.spcInfo);
	this.chanceZeroDf = .2;
	this.drivingForceSensitivity = 1;//formalize this a bit
	this.updateListenerName = this.type + this.handle;
	this.setupUpdate(this.spcInfo, this.dataGas, this.dataLiq, this.actCoeffFuncs, this.drivingForce, this.updateListenerName, this.drawList, this.dotMgrLiq, this.wallLiq, this.numAbs, this.drivingForceSensitivity, this.numEjt, this.wallGas, this.wallPtIdxs)
	
	//this.setupStd();
}

_.extend(Liquid.prototype, objectFuncs, {
	makeActCoeffFuncs: function(type, info, spcInfo) {
		if (type == 'twoSfxMrg') {
			var coeff = info.a
			return this.makeTwoSfxMrgFuncs(spcInfo, coeff);
		}
	},
	makeTwoSfxMrgFuncs: function(spcInfo, coeff) {
		var funcs = {};
		var gasConst = R;
		for (var spcName in spcInfo) {
			funcs[spcName] = function(x, T) {
				return Math.exp(coeff * x * x / (gasConst * T)); 
			}
		}
		return funcs;
	},
	makeWallLiq: function(spcInfo, wallGas, wallPtIdxs, dotMgrLiq, handle) {
		var vol = this.getLiqWallVol(spcInfo);
		//liq wall needs to go in opposite direction of gas wall
		var pts = this.getWallLiqPts(wallGas, wallPtIdxs, vol);
		var handler = {func: this.hit, obj: this};
		return window.walls.addWall({pts:pts, handler:handler, handle: 'liquid' + this.handle.toCapitalCamelCase(), record: false, show: true, dotManager: dotMgrLiq, close: false});
	},
	makeNumAbsorbed: function(spcInfo) {
		var nA = {};
		for (var spcName in spcInfo) {
			nA[spcName] = 0;
		}
		return nA;
	},
	getLiqWallVol: function(spcInfo, dotMgr) {
		var vol = 0;
		if (!dotMgr) {
			for (var spcName in spcInfo) {
				vol += spcInfo[spcName].spcVol * spcInfo[spcName].count / N;
			}
		} else {
			for (var spcName in spcInfo) {
				vol += spcInfo[spcName].spcVol * dotMgr.get({spcName: spcName}).length / N;
			}
		}
		return vol;
	},
	getWallLiqPts: function(wallGas, wallGasPts, vol) {
		var a = wallGas[wallGasPts[0]];
		var b = wallGas[wallGasPts[1]];
		var bottomLeft = a.x < b.x ? a : b;
		var bottomRight = bottomLeft == a ? b : a;
		var height = this.getWallHeight(bottomRight.x - bottomLeft.x, vol);
		var topLeft = bottomLeft.copy().movePt(V(0, -height));
		var topRight = bottomRight.copy().movePt(V(0, -height));
		return [topRight, topLeft];
	},
	getWallHeight: function(width, vol) {
		return vol/(vConst*width);
	},
	makeDotManager: function(spcInfo) {
		var dotMgr = new DotManager();
		for (var spcName in spcInfo) {
			dotMgr.addSpcs(spcName);
		}
		return dotMgr;
	},
	makeDots: function(wallLiq, wallGas, wallGasIdxs, makeOrder, temp, dotMgrLiq) {
		var pos = wallLiq[1].copy();
		var b = P(Math.max(wallGas[wallGasIdxs[0]].x, wallGas[wallGasIdxs[1]].x), wallGas[wallGasIdxs[0]].y);
		var dims = pos.VTo(b);
		
		for (var spcName in makeOrder) {
			var order = makeOrder[spcName]
			spcs[spcName].populate(pos, dims, order.count, temp, wallLiq.handle, wallLiq.handle, dotMgrLiq); 
		}
	},
	initData: function(wall, spcInfo, extras) {
		var data = {};
		if (extras) {
			for (var extraIdx=0; extraIdx<extras.length; extraIdx++) {
				var extra = extras[extraIdx];
				var dataObj = wall.getDataObj(extra, undefined, true);
				if (dataObj === false) {
					var recordFunc = 'record' + extra.toCapitalCamelCase();
					wall[recordFunc]();
					data[extra] = wall.getDataObj(extra).src();
				} else {
					data[extra] = dataObj.src();
				}
			}
		}
		for (var spcName in spcInfo) {
			var attrs = {spcName: spcName, tag: wall.handle};
			wall.recordFrac(attrs)
			data[spcName] = wall.getDataObj('frac', attrs).src();
		}
		return data;
	},
	makeSpcInfo: function(spcInfo) {
		var reformat = {};
		for (var infolet in spcInfo) {
			var spc = spcInfo[infolet];
			reformat[infolet] = new this.Species(infolet, spc.count, spc.hVap, spc.cP, spc.spcVol, spc.antoineCoeffs);
		}
		return reformat;
	},
	makeDrivingForce: function(spcInfo) {
		var force = {};
		for (var spcName in spcInfo) {
			force[spcName] = 0;
		}
		return force;
	},
	setupUpdate: function(spcInfo, dataGas, dataLiq, actCoeffFuncs, drivingForce, listenerName, drawList, dotMgrLiq, wallLiq, numAbs, drivingForceSensitivity, numEjt, wallGas, wallGasIdxs) {
		this.calcCp = this.setupCalcCp(spcInfo, dotMgrLiq);
		this.calcEquil = this.setupUpdateEquil(spcInfo, dataGas, dataLiq, actCoeffFuncs, drivingForce);
		this.drawDots = this.setupDrawDots(drawList);
		this.moveDots = this.setupMoveDots(dotMgrLiq, spcInfo, wallLiq, wallGas, wallGasIdxs);
		this.ejectDots = this.setupEjectDots(dotMgrLiq, spcInfo, drivingForce, numAbs, drivingForceSensitivity, drawList, wallLiq, numEjt);//you were making the liquid eject if df<0 whether hit or not
		var sizeWall = this.setupSizeWall(wallLiq, wallGas, spcInfo, dotMgrLiq, wallGasIdxs)
		var zeroAttrs = this.zeroAttrs;
		var calcCp = this.calcCp, calcEquil = this.calcEquil, drawDots = this.drawDots, moveDots = this.moveDots, ejectDots = this.ejectDots;
		calcCp();
		addListener(curLevel, 'update', listenerName, function() {
			calcCp();
			calcEquil();
			drawDots();
			moveDots();
			ejectDots();
			sizeWall();
		})
	},
	setupCalcCp: function(spcInfo, dotMgrLiq) {
		var self = this;
		return function() {
			var Cp = 0;
			for (var spcName in spcInfo) {
				Cp += spcInfo[spcName].cP * dotMgrLiq.get({spcName: spcName}).length / N; //Cp in J/K  spcInfo given in J/mol-k
			}
			self.Cp = Cp;	
		}

	},
	zeroAttrs: function(a) {
		for (var aLet in a) {
			a[aLet] = 0;
		}
	},
	setupUpdateEquil: function(spcInfo, dataGas, dataLiq, actCoeffFuncs, drivingForce) {
		var self = this;
		return function() {
			for (var spcName in spcInfo) {
				var spc = spcInfo[spcName];
				var gasFrac = dataGas[spcName][dataGas[spcName].length - 1];
				var liqFrac = dataLiq[spcName][dataLiq[spcName].length - 1];
				var liqTemp = self.temp;
				var actCoeff = actCoeffFuncs[spcName](liqFrac, liqTemp);
				var antCoeffs = spcInfo[spcName].antoineCoeffs;
				var pPure = self.getPPure(antCoeffs.a, antCoeffs.b, antCoeffs.c, liqTemp);
				var pEq = pPure * actCoeff * liqFrac;
				var pGas = gasFrac * dataGas.pInt[dataGas.pInt.length - 1];
				drivingForce[spcName] = pGas - pEq;
			}
		}
	},
	setupDrawDots: function(drawList) {
		return function() {
			window.draw.dotsAsst(drawList);
		};
	},
	setupMoveDots: function(dotMgr, spcInfo, wallLiq, wallGas, wallGasIdxs) {
		var self = this;
		
		var dotLists = [];
		for (var spcName in spcInfo) {
			dotLists.push(dotMgr.get({spcName: spcName}));
		}
		return function() {
			var stepSize, i, len, moveVec, dotMass, numGroups;
			//reducing number of vectors I have to make while making motion look random by stepping at random intervals

			var xMax = wallLiq[0].x;
			var xMin = wallLiq[1].x;
			var yMax = wallGas[wallGasIdxs[0]].y;
			var yMin = wallLiq[0].y;
			
			for (var listIdx=0; listIdx<dotLists.length; listIdx++) {
				var dots = dotLists[listIdx];
				len = dots.length;
				if (len) {
					dotMass = dots[0].m;
					stepSize = 15 + Math.ceil(Math.random() * 15);
					for (var groupNum=0; groupNum<stepSize; groupNum++) {
						var mag = tempToV(dotMass, self.temp);
						var dir = Math.PI * 2 * Math.random();
						moveVec = V(Math.cos(dir) * mag, Math.sin(dir) * mag);
						for (var dotIdx=groupNum; dotIdx<len; dotIdx+=stepSize) {
							dots[dotIdx].x = Math.max(xMin, Math.min(xMax, dots[dotIdx].x + moveVec.dx));
							dots[dotIdx].y = Math.max(yMin, Math.min(yMax, dots[dotIdx].y + moveVec.dy));
						}
					}
				}
			}
		};

		
	},
	setupEjectDots: function(dotMgrLiq, spcInfo, drivingForce, numAbs, drivingForceSensitivity, drawList, wallLiq, numEjt) {
		var self = this;
		var wallGas = this.wallGas;
		var dotLists = {};
		for (var spcName in spcInfo) {
			dotLists[spcName] = dotMgrLiq.get({spcName: spcName});
		}
		return function() {
			for (var spcName in spcInfo) {
				var dF = drivingForce[spcName];
				var abs = numAbs[spcName];
				//converges to abs as df -> 0
				
				if (dF > 0) { 
					numEjt[spcName] += abs / (dF * drivingForceSensitivity + 1);
				} else {
					//numEjt[spcName] += abs * (-dF * drivingForceSensitivity + 1);
					numEjt[spcName] += (wallLiq[0].x - wallLiq[1].x) * -dF * drivingForceSensitivity / 1000;
				}
				//numEjt[spcName] = 1;
				numEjt[spcName] = Math.min(numEjt[spcName], dotLists[spcName].length);
				var flr = Math.floor(numEjt[spcName])
				
				if (flr) {
					self.eject(dotMgrLiq, window.dotManager, spcInfo, spcName, flr, wallGas, drawList, wallLiq);
					numEjt[spcName] = 0;
				}
			}
		}
	},
	setupSizeWall: function(wallLiq, wallGas, spcInfo, dotMgrLiq, wallGasIdxs) {
		var self = this;
		return function() {<commit_msg>liquid comes to equilibrium<commit_after>function Liquid(attrs) {
	//driving force convention: positive -> into liquid, neg -> into vapor
	//still need to do pInt adjustment for wallGas
	this.type = 'Liquid';
	this.wallInfo = attrs.wallInfo;
	this.wallGas = walls[this.wallInfo];
	this.wallPtIdxs = attrs.wallPts || [this.wallGas.length - 2, this.wallGas.length - 3];
	this.handle = attrs.handle;
	this.cleanUpWith = attrs.cleanUpWith || currentSetupType;
	this.actCoeffType = attrs.actCoeffType; //twoSfxMrg for two suffix margules, 
	this.actCoeffInfo = attrs.actCoeffInfo;
	this.Cp = 0; //to be set each turn/absorption and used in setting temperatures for reflecting dots.  Capital C denotes total heat capacity.
	var tempInit = attrs.tempInit;
	this.temp = tempInit;
	this.spcInfo = this.makeSpcInfo(attrs.spcInfo); //formatted as {spc1: {count: #, spcVol: #, cP: #, antoineCoeffs: {a: #,b: #,c: #}, hVap: #} ... } spcVol in L/mol, hVap in kj/mol
	this.drivingForce = this.makeDrivingForce(this.spcInfo);
	this.dotMgrLiq = this.makeDotManager(this.spcInfo);
	this.wallLiq = this.makeWallLiq(this.spcInfo, this.wallGas, this.wallPtIdxs, this.dotMgrLiq);
	this.numAbs = this.makeNumAbsorbed(this.spcInfo);
	this.numEjt = deepCopy(this.numAbs);
	this.makeDots(this.wallLiq, this.wallGas, this.wallPtIdxs, this.spcInfo, tempInit, this.dotMgrLiq) && this.deleteCount(this.spcInfo);
	this.dataGas = this.initData(this.wallGas, this.spcInfo, ['pInt', 'temp']);
	this.dataLiq = this.initData(this.wallLiq, this.spcInfo);
	this.drawList = this.makeDrawList(this.dotMgrLiq); //need to make draw list in random order otherwise dots drawn on top will look more prominant than they are.
	this.actCoeffFuncs = this.makeActCoeffFuncs(this.actCoeffType, this.actCoeffInfo, this.spcInfo);
	this.chanceZeroDf = .4;
	this.drivingForceSensitivity = 10;//formalize this a bit
	this.updateListenerName = this.type + this.handle;
	this.setupUpdate(this.spcInfo, this.dataGas, this.dataLiq, this.actCoeffFuncs, this.drivingForce, this.updateListenerName, this.drawList, this.dotMgrLiq, this.wallLiq, this.numAbs, this.drivingForceSensitivity, this.numEjt, this.wallGas, this.wallPtIdxs)
	
	//this.setupStd();
}

_.extend(Liquid.prototype, objectFuncs, {
	makeActCoeffFuncs: function(type, info, spcInfo) {
		if (type == 'twoSfxMrg') {
			var coeff = info.a
			return this.makeTwoSfxMrgFuncs(spcInfo, coeff);
		}
	},
	makeTwoSfxMrgFuncs: function(spcInfo, coeff) {
		var funcs = {};
		var gasConst = R;
		for (var spcName in spcInfo) {
			funcs[spcName] = function(x, T) {
				return Math.exp(coeff * x * x / (gasConst * T)); 
			}
		}
		return funcs;
	},
	makeWallLiq: function(spcInfo, wallGas, wallPtIdxs, dotMgrLiq, handle) {
		var vol = this.getLiqWallVol(spcInfo);
		//liq wall needs to go in opposite direction of gas wall
		var pts = this.getWallLiqPts(wallGas, wallPtIdxs, vol);
		var handler = {func: this.hit, obj: this};
		return window.walls.addWall({pts:pts, handler:handler, handle: 'liquid' + this.handle.toCapitalCamelCase(), record: false, show: true, dotManager: dotMgrLiq, close: false});
	},
	makeNumAbsorbed: function(spcInfo) {
		var nA = {};
		for (var spcName in spcInfo) {
			nA[spcName] = 0;
		}
		return nA;
	},
	getLiqWallVol: function(spcInfo, dotMgr) {
		var vol = 0;
		if (!dotMgr) {
			for (var spcName in spcInfo) {
				vol += spcInfo[spcName].spcVol * spcInfo[spcName].count / N;
			}
		} else {
			for (var spcName in spcInfo) {
				vol += spcInfo[spcName].spcVol * dotMgr.get({spcName: spcName}).length / N;
			}
		}
		return vol;
	},
	getWallLiqPts: function(wallGas, wallGasPts, vol) {
		var a = wallGas[wallGasPts[0]];
		var b = wallGas[wallGasPts[1]];
		var bottomLeft = a.x < b.x ? a : b;
		var bottomRight = bottomLeft == a ? b : a;
		var height = this.getWallHeight(bottomRight.x - bottomLeft.x, vol);
		var topLeft = bottomLeft.copy().movePt(V(0, -height));
		var topRight = bottomRight.copy().movePt(V(0, -height));
		return [topRight, topLeft];
	},
	getWallHeight: function(width, vol) {
		return vol/(vConst*width);
	},
	makeDotManager: function(spcInfo) {
		var dotMgr = new DotManager();
		for (var spcName in spcInfo) {
			dotMgr.addSpcs(spcName);
		}
		return dotMgr;
	},
	makeDots: function(wallLiq, wallGas, wallGasIdxs, makeOrder, temp, dotMgrLiq) {
		var pos = wallLiq[1].copy();
		var b = P(Math.max(wallGas[wallGasIdxs[0]].x, wallGas[wallGasIdxs[1]].x), wallGas[wallGasIdxs[0]].y);
		var dims = pos.VTo(b);
		
		for (var spcName in makeOrder) {
			var order = makeOrder[spcName]
			spcs[spcName].populate(pos, dims, order.count, temp, wallLiq.handle, wallLiq.handle, dotMgrLiq); 
		}
	},
	initData: function(wall, spcInfo, extras) {
		var data = {};
		if (extras) {
			for (var extraIdx=0; extraIdx<extras.length; extraIdx++) {
				var extra = extras[extraIdx];
				var dataObj = wall.getDataObj(extra, undefined, true);
				if (dataObj === false) {
					var recordFunc = 'record' + extra.toCapitalCamelCase();
					wall[recordFunc]();
					data[extra] = wall.getDataObj(extra).src();
				} else {
					data[extra] = dataObj.src();
				}
			}
		}
		for (var spcName in spcInfo) {
			var attrs = {spcName: spcName, tag: wall.handle};
			wall.recordFrac(attrs)
			data[spcName] = wall.getDataObj('frac', attrs).src();
		}
		return data;
	},
	makeSpcInfo: function(spcInfo) {
		var reformat = {};
		for (var infolet in spcInfo) {
			var spc = spcInfo[infolet];
			reformat[infolet] = new this.Species(infolet, spc.count, spc.hVap, spc.cP, spc.spcVol, spc.antoineCoeffs);
		}
		return reformat;
	},
	makeDrivingForce: function(spcInfo) {
		var force = {};
		for (var spcName in spcInfo) {
			force[spcName] = 0;
		}
		return force;
	},
	setupUpdate: function(spcInfo, dataGas, dataLiq, actCoeffFuncs, drivingForce, listenerName, drawList, dotMgrLiq, wallLiq, numAbs, drivingForceSensitivity, numEjt, wallGas, wallGasIdxs) {
		this.calcCp = this.setupCalcCp(spcInfo, dotMgrLiq);
		this.calcEquil = this.setupUpdateEquil(spcInfo, dataGas, dataLiq, actCoeffFuncs, drivingForce);
		this.drawDots = this.setupDrawDots(drawList);
		this.moveDots = this.setupMoveDots(dotMgrLiq, spcInfo, wallLiq, wallGas, wallGasIdxs);
		this.ejectDots = this.setupEjectDots(dotMgrLiq, spcInfo, drivingForce, numAbs, drivingForceSensitivity, drawList, wallLiq, numEjt);//you were making the liquid eject if df<0 whether hit or not
		var sizeWall = this.setupSizeWall(wallLiq, wallGas, spcInfo, dotMgrLiq, wallGasIdxs)
		var zeroAttrs = this.zeroAttrs;
		var calcCp = this.calcCp, calcEquil = this.calcEquil, drawDots = this.drawDots, moveDots = this.moveDots, ejectDots = this.ejectDots;
		calcCp();
		addListener(curLevel, 'update', listenerName, function() {
			calcCp();
			calcEquil();
			drawDots();
			moveDots();
			ejectDots();
			sizeWall();
		})
	},
	setupCalcCp: function(spcInfo, dotMgrLiq) {
		var self = this;
		return function() {
			var Cp = 0;
			for (var spcName in spcInfo) {
				Cp += spcInfo[spcName].cP * dotMgrLiq.get({spcName: spcName}).length / N; //Cp in J/K  spcInfo given in J/mol-k
			}
			self.Cp = Cp;	
		}

	},
	zeroAttrs: function(a) {
		for (var aLet in a) {
			a[aLet] = 0;
		}
	},
	setupUpdateEquil: function(spcInfo, dataGas, dataLiq, actCoeffFuncs, drivingForce) {
		var self = this;
		return function() {
			for (var spcName in spcInfo) {
				var spc = spcInfo[spcName];
				var gasFrac = dataGas[spcName][dataGas[spcName].length - 1];
				var liqFrac = dataLiq[spcName][dataLiq[spcName].length - 1];
				var liqTemp = self.temp;
				var actCoeff = actCoeffFuncs[spcName](liqFrac, liqTemp);
				var antCoeffs = spcInfo[spcName].antoineCoeffs;
				var pPure = self.getPPure(antCoeffs.a, antCoeffs.b, antCoeffs.c, liqTemp);
				var pEq = pPure * actCoeff * liqFrac;
				var pGas = gasFrac * dataGas.pInt[dataGas.pInt.length - 1];
				drivingForce[spcName] = pGas - pEq;
			}
		}
	},
	setupDrawDots: function(drawList) {
		return function() {
			window.draw.dotsAsst(drawList);
		};
	},
	setupMoveDots: function(dotMgr, spcInfo, wallLiq, wallGas, wallGasIdxs) {
		var self = this;
		
		var dotLists = [];
		for (var spcName in spcInfo) {
			dotLists.push(dotMgr.get({spcName: spcName}));
		}
		return function() {
			var stepSize, i, len, moveVec, dotMass, numGroups;
			//reducing number of vectors I have to make while making motion look random by stepping at random intervals

			var xMax = wallLiq[0].x;
			var xMin = wallLiq[1].x;
			var yMax = wallGas[wallGasIdxs[0]].y;
			var yMin = wallLiq[0].y;
			
			for (var listIdx=0; listIdx<dotLists.length; listIdx++) {
				var dots = dotLists[listIdx];
				len = dots.length;
				if (len) {
					dotMass = dots[0].m;
					stepSize = 15 + Math.ceil(Math.random() * 15);
					for (var groupNum=0; groupNum<stepSize; groupNum++) {
						var mag = tempToV(dotMass, self.temp);
						var dir = Math.PI * 2 * Math.random();
						moveVec = V(Math.cos(dir) * mag, Math.sin(dir) * mag);
						for (var dotIdx=groupNum; dotIdx<len; dotIdx+=stepSize) {
							dots[dotIdx].x = Math.max(xMin, Math.min(xMax, dots[dotIdx].x + moveVec.dx));
							dots[dotIdx].y = Math.max(yMin, Math.min(yMax, dots[dotIdx].y + moveVec.dy));
						}
					}
				}
			}
		};

		
	},
	setupEjectDots: function(dotMgrLiq, spcInfo, drivingForce, numAbs, drivingForceSensitivity, drawList, wallLiq, numEjt) {
		var self = this;
		var wallGas = this.wallGas;
		var dotLists = {};
		for (var spcName in spcInfo) {
			dotLists[spcName] = dotMgrLiq.get({spcName: spcName});
		}
		return function() {
			for (var spcName in spcInfo) {
				var dF = drivingForce[spcName];
				var abs = numAbs[spcName];
				numAbs[spcName] = 0;
				//converges to abs as df -> 0
				
				if (dF > 0) { 
					numEjt[spcName] += abs / (dF * drivingForceSensitivity + 1);
				} else {
					numEjt[spcName] += 1 + Math.sqrt(abs * (-dF * drivingForceSensitivity + 1));
					numEjt[spcName] += (wallLiq[0].x - wallLiq[1].x) * -dF * drivingForceSensitivity / 2000;
				}
				//numEjt[spcName] = 1;
				numEjt[spcName] = Math.min(numEjt[spcName], dotLists[spcName].length);
				var flr = Math.floor(numEjt[spcName])
				
				if (flr) {
					self.eject(dotMgrLiq, window.dotManager, spcInfo, spcName, flr, wallGas, drawList, wallLiq);
					numEjt[spcName] = 0;
				}
			}
		}
	},
	setupSizeWall: function(wallLiq, wallGas, spcInfo, dotMgrLiq, wallGasIdxs) {
		var self = this;
		return function() {
{'fraction_non_alphanumeric': 0.14016015530211115, 'fraction_numerical': 0.004804659063334142, 'mean_word_length': 3.889890840056953, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1141696', 'n_tokens_mistral': 9101, 'n_tokens_neox': 8100, 'n_words': 1726}