public override FlowResponseData getFlowDriverSourcePossibleValues(FlowCalculationData baseData, FlowDriverModifier modifier) { pumpingPercent = 1.0; FlowResponseData normalResponse = base.getFlowDriverSourcePossibleValues(baseData, modifier); lastSourcePossibleValue = normalResponse; return normalResponse; }
public override FlowResponseData getFlowDriverDeliveryPossibleValues(FlowCalculationData baseData, FlowDriverModifier modifier) { pumpingPercent = lastSourcePossibleValue.flowPercent; mcrPressure = lastSourcePossibleValue.backPressure; FlowResponseData normalResponse = base.getFlowDriverDeliveryPossibleValues(baseData, modifier); double deltaP = lastSourcePossibleValue.backPressure - normalResponse.backPressure; if (deltaP < minDeltaP) { normalResponse.flowPercent = 0.0; } else { double flowPercent = 1.0; //Default to 1, in case deltaP > maxDeltaP, in which case, we do 100% of what we can. if (deltaP < maxDeltaP) { flowPercent = (deltaP - minDeltaP) / (maxDeltaP - minDeltaP); } normalResponse.flowPercent = Math.Min(normalResponse.flowPercent, lastSourcePossibleValue.flowPercent) * flowPercent; } normalResponse.flowVolume = baseData.desiredFlowVolume * normalResponse.flowPercent; return normalResponse; }
public override FlowResponseData getSourcePossibleValues(FlowCalculationData baseData, FlowComponent caller, double flowPercent, double pressurePercent) { double limitedFlowPercent = getLimitedFlowPercent(baseData, caller, flowPercent); double limitedPressurePercent = getLimitedPressurePercent(baseData, caller, flowPercent, pressurePercent); FlowResponseData ret = sourceComponent.getSourcePossibleValues(baseData, this, limitedFlowPercent, limitedPressurePercent); if (ret != null) { setPressuresForSourceSide(baseData.pressure, ret.backPressure, limitedPressurePercent, pressurePercent); } return ret; }
public void connectComponents() { foreach(FlowComponent iter in components.Values) { iter.connectSelf(components); } foreach(FlowDriver iter in flowDrivers.Values) { FlowCalculationData baseData = new FlowCalculationData(iter, angerMap, 0); baseData.flowDriver = iter; iter.exploreDeliveryGraph(baseData, null); iter.exploreSourceGraph(baseData, null); flowDriverModifiers[iter.name] = new FlowDriverModifier(); } }
public bool applySolution(FlowCalculationData baseData, FlowDriverModifier modifier, bool lastTime) { if (solutionApplied) return true; //If we have already successfully sent all our data, then we shouldn't do it again... SettingResponseData sourceResponse = setSourceValues(baseData, null, pumpingPercent * mcrRating * modifier.flowPercent, lastTime); if (sourceResponse != null) { baseData.applySourceResponse(sourceResponse); setDeliveryValues(baseData, null, pumpingPercent * mcrRating * modifier.flowPercent, lastTime); solutionApplied = true; } return solutionApplied; }
public override FlowResponseData getDeliveryPossibleValues(FlowCalculationData baseData, FlowComponent caller, double flowPercent, double pressurePercent) { if (caller == null) { //This shouldn't happen anymore, since GraphSolver calls the pump-specific (i.e. not override) version. baseData.flowDriver = this; baseData.desiredFlowVolume = pumpingPercent * mcrRating * flowPercent; baseData.pressure = mcrPressure * pressurePercent; outletPressure = baseData.pressure; return deliveryComponent.getDeliveryPossibleValues(baseData, this, 1.0, 1.0); //Always ask 100% of whatever desired flow we have } else { return null; //TODO: Handle this case when pumps are in series } }
public override FlowResponseData getDeliveryPossibleValues(FlowCalculationData baseData, FlowComponent caller, double flowPercent, double pressurePercent) { FlowResponseData ret = new FlowResponseData(); ret.backPressure = tankPressure; if (isSealed) { double deltaPressure = baseData.pressure - tankPressure; if(deltaPressure < 0.0) { //There is too much back-pressure for this pump to overcome it. Thus, nothing will flow in. ret.flowPercent = 0.0; } else if(deltaPressure > normalPressureDelta || normalPressureDelta == 0.0) //Don't allow else to divide by 0 { //We don't add any restriction, since the dP is greater than our defined 'normal'. ret.flowPercent = flowPercent; } else { //Here we need to do a linear interpolation between deltaPressure = normalPressure meaning 1, and deltaPressure = 0 meaning 0. //Thus, at normalPressure, we get full flow, and at no pressure difference, we get no flow. ret.flowPercent = deltaPressure / normalPressureDelta * flowPercent; } } else { if (currentVolume < double.MaxValue) //TODO: Make it so that tanks can't overflow, especially sealed tanks... Right now, it will pretty much always accept any flow that we want to put into it... { ret.flowPercent = flowPercent; //Allow everything that they are asking for, since we don't do restrictions inside the tank. } else { ret.flowPercent = 0.0; } } ret.flowVolume = flowPercent * baseData.desiredFlowVolume; ret.fluidTypeMap = currentFluidTypeMap; outletPressure = ret.backPressure; return ret; }
private double getLimitedPressurePercent(FlowCalculationData baseData, FlowComponent caller, double flowPercent, double pressurePercent) { double limitedPressurePercent = pressurePercent * normalPressureDropPercent; if(flowPercent == 0.0) { limitedPressurePercent = 0.0; } else if(flowAllowedPercent < flowPercent) { //limitedPressurePercent *= flowAllowedPercent / flowPercent; //If flowPercent is included, then splitters with different maxes don't work well... limitedPressurePercent *= flowAllowedPercent; } return limitedPressurePercent; }
public override void exploreDeliveryGraph(FlowCalculationData baseData, FlowComponent caller) { deliveryComponent.exploreDeliveryGraph(baseData, this); }
public abstract SettingResponseData setSourceValues(FlowCalculationData baseData, FlowComponent caller, double flowVolume, bool lastTime);
private double getLimitedFlowPercent(FlowCalculationData baseData, FlowComponent caller, double flowPercent) { double limitedFlowPercent = Math.Min(flowPercent, flowAllowedPercent); if (baseData.desiredFlowVolume * limitedFlowPercent > maxFlow) { //Need to cut down even further, so that we don't go over our maximum. limitedFlowPercent = maxFlow / baseData.desiredFlowVolume; } //Apply anger effects, if they are needed. if (baseData.angerMap.ContainsKey(name)) { limitedFlowPercent *= baseData.angerMap[name]; } return limitedFlowPercent; }
public override void setDeliveryValues(FlowCalculationData baseData, FlowComponent caller, double flowVolume, bool lastTime) { finalFlow += flowVolume; if(lastTime) { double volumeToAdd = flowVolume * PhysTools.timeStep; SettingResponseData tankCurrentFluids = new SettingResponseData(currentTemperature, this.currentFluidTypeMap); SettingResponseData incomingFluids = new SettingResponseData(baseData.temperature, baseData.fluidTypeMap); SettingResponseData mixture = PhysTools.mixFluidPercentsAndTemperatures(new SettingResponseData[] { tankCurrentFluids, incomingFluids }, new double[] { currentVolume, volumeToAdd }); this.currentTemperature = mixture.temperature; this.currentFluidTypeMap = mixture.fluidTypeMap; if(isSealed) { currentVolume += volumeToAdd; //Now, we need to calculate the pressure. double gasVolume = 0.0; double liquidVolume = 0.0; PhysTools.normalizeFluidMixture(ref currentFluidTypeMap); //Make sure that the percentages are normalized, otherwise very bad things would happen foreach (KeyValuePair<FluidType, double> kvp in currentFluidTypeMap) { //NOTE: If we have multiple gasses that need to compress differently (I don't think that is possible, even with AIR and STEAM), then we need to re-think this algorithm from the ground up. if (kvp.Key.isGas) gasVolume += kvp.Value * currentVolume; else liquidVolume += kvp.Value * currentVolume; } percentFilled = liquidVolume / capacity; double volumeForGas = capacity - liquidVolume; tankPressure = (gasVolume / volumeForGas) - 1.0; //Subtract 1.0 to convert from bara to barg. } else { double adjustmentToMake = 0.0; //Vent any gasses out the top. foreach(FluidType key in currentFluidTypeMap.Keys.ToList<FluidType>()) { if(key.isGas) { adjustmentToMake += currentFluidTypeMap[key]; currentFluidTypeMap.Remove(key); } } if (adjustmentToMake > 0.0) { //We ended up removing some gas, so we need to re-normalize, and adjust the values PhysTools.normalizeFluidMixture(ref currentFluidTypeMap); volumeToAdd *= (1.0 - adjustmentToMake); //Remove the volume of gas that is escaping } tankPressure = 0.4; //TODO: Adjust non-sealed tank outlet pressures based on size/shape, etc. currentVolume += volumeToAdd; percentFilled = currentVolume / capacity; } } inletTemperature = currentTemperature; outletTemperature = currentTemperature; }
public override void exploreSourceGraph(FlowCalculationData baseData, FlowComponent caller) { //Don't have to do anything, since this is the end of the line }
private double[] modifyPreferenceBasedOnBackPressures(double[] preferredFlowPercent, FlowResponseData[] responses, FlowCalculationData baseData) { if (baseData.pressure == 0.0) return preferredFlowPercent; //We don't want to have a divide by zero down below. //1.0 - backpressure/mcr-pressure seems to be a good starting guess for a scaler. double sumBeforeModification = 0.0; for(int i = 0; i < preferredFlowPercent.Length; i++) { sumBeforeModification += preferredFlowPercent[i]; double modifier = Math.Max(1.0 - (responses[i].backPressure / baseData.pressure), 0.000001); //Don't let the number go completely to zero. preferredFlowPercent[i] *= modifier; } return PhysTools.normalizePercentArray(preferredFlowPercent, sumBeforeModification); }
public override void setDeliveryValues(FlowCalculationData baseData, FlowComponent caller, double flowVolume, bool lastTime) { finalFlow = flowVolume; deliveryComponent.setDeliveryValues(baseData, this, flowVolume, lastTime); currentFluidTypeMap = baseData.fluidTypeMap; //On the delivery side, the mixture comes from passed in arguments inletTemperature = baseData.temperature; outletTemperature = baseData.temperature; }
private FlowResponseData calculateCombiningFunctionality(FlowCalculationData baseData, FlowComponent caller, double flowPercent, FlowComponent[] nodes, bool isDeliverySide, double pressurePercent) { if (!combinerMap.ContainsKey(baseData.flowDriver.name)) { double[] toAdd = new double[indexByName.Count]; for (int i = 0; i < indexByName.Count; i++) { if (!indexesUsedByPump.ContainsKey(baseData.flowDriver.name) || indexesUsedByPump[baseData.flowDriver.name][i]) toAdd[i] = -1.0; //Initialize with all negative numbers, and wait until they are not negative to know when we are done. else toAdd[i] = 0.0; //Initialize to 0 if this pump will never get this number } combinerMap[baseData.flowDriver.name] = toAdd; } double[] percentMap = combinerMap[baseData.flowDriver.name]; int index = indexByName[caller.name]; percentMap[index] = flowPercent; double percentSum = 0.0; for (int i = 0; i < percentMap.Length; i++) { if (percentMap[i] < 0.0) return null; //We haven't seen all of the inputs to combine them... thus, return null until we do see them all. else percentSum += percentMap[i]; } if (percentSum > 1.0) percentSum = 1.0; if (!combinerDownstreamValues.ContainsKey(baseData.flowDriver.name)) { if (isDeliverySide) combinerDownstreamValues[baseData.flowDriver.name] = deliveryComponents[0].getDeliveryPossibleValues(baseData, this, percentSum, pressurePercent); else combinerDownstreamValues[baseData.flowDriver.name] = sourceComponents[0].getSourcePossibleValues(baseData, this, percentSum, pressurePercent); } FlowResponseData ret = combinerDownstreamValues[baseData.flowDriver.name].clone(); lastCombinePercent = ret.flowPercent; if (percentSum != 0.0) { ret.flowPercent *= (percentMap[index] / percentSum); //The divide here is to normalize it. ret.flowVolume *= (percentMap[index] / percentSum); } if (!flowPercentageSolutions.ContainsKey(baseData.flowDriver.name)) flowPercentageSolutions[baseData.flowDriver.name] = new double[1]; flowPercentageSolutions[baseData.flowDriver.name][0] = ret.flowPercent; setPressures(baseData.pressure, ret.backPressure, pressurePercent, isDeliverySide); return ret; }
private FlowResponseData calculateSplittingFunctionality(FlowCalculationData baseData, double flowPercent, FlowComponent[] nodes, bool isDeliverySide, double pressurePercent) { FlowResponseData[] responses = new FlowResponseData[nodes.Length]; bool foundNull = false; double maxBackPressure = 0.0; for (int attempt = 0; attempt < 2; attempt++) { //Most of the time, we will need to ask twice, because some will be null the first time. foundNull = false; for (int i = 0; i < nodes.Length; i++) { if (responses[i] == null) { if (isDeliverySide) responses[i] = nodes[i].getDeliveryPossibleValues(baseData, this, Math.Min(flowPercent, maxWeights[i]), pressurePercent); else responses[i] = nodes[i].getSourcePossibleValues(baseData, this, Math.Min(flowPercent, maxWeights[i]), pressurePercent); } if (responses[i] == null) foundNull = true; else if (responses[i].backPressure > maxBackPressure) maxBackPressure = responses[i].backPressure; } if (!foundNull) break; } if (foundNull) { return null; //We weren't able to get everything, and this is probably because there is another splitter up stream that will retry us again. } if (!flowPercentageSolutions.ContainsKey(baseData.flowDriver.name)) flowPercentageSolutions[baseData.flowDriver.name] = new double[nodes.Length]; //If we get here, then we have all the info we should need to solve ourselves. double desiredPercent = flowPercent; double[] maxFlowPercent = new double[nodes.Length]; double[] preferredFlowPercent = new double[nodes.Length]; double percentToReturn = 0.0; double sumOfMaxFlow = 0; double currentSum = 0.0; if (linkedCombiner != null) { desiredPercent = Math.Min(linkedCombiner.lastCombinePercent, flowPercent); //All are equal... grab first. This is the combiner's maxFlow double[] flowPercentToCombiner; //NOTE: Both solutions with both divisors gave the same answer, and I found the bug elsewhere... //TODO: Determing what divisor we actually want to use. if (linkedCombiner.combinerMap.ContainsKey(baseData.flowDriver.name)) flowPercentToCombiner = linkedCombiner.combinerMap[baseData.flowDriver.name]; else { flowPercentToCombiner = new double[nodes.Length]; for (int i = 0; i < nodes.Length; i++) flowPercentToCombiner[i] = responses[i].flowPercent; //This was my original solution, but the map is better. } double tempSum = 0.0; for (int i = 0; i < nodes.Length; i++) { maxFlowPercent[i] = Math.Min(maxWeights[i], flowPercentToCombiner[i]); preferredFlowPercent[i] = Math.Min(normalWeights[i], maxFlowPercent[i]) * desiredPercent; tempSum += preferredFlowPercent[i]; } bool modifySplitBasedOnBackPressures = true; //I can foresee this wanting to be an optional parameter, sometimes doing it, and sometimes not. if (modifySplitBasedOnBackPressures) { preferredFlowPercent = modifyPreferenceBasedOnBackPressures(preferredFlowPercent, responses, baseData); } preferredFlowPercent = fixPercents(preferredFlowPercent, maxFlowPercent, desiredPercent); //Normalize preferredFlowPercent (since we know the final flow as desiredPercent, we can safely do so in this case) for (int i = 0; i < nodes.Length; i++) { currentSum += preferredFlowPercent[i]; percentToReturn += preferredFlowPercent[i]; sumOfMaxFlow += maxFlowPercent[i]; } } else { desiredPercent = flowPercent; for (int i = 0; i < nodes.Length; i++) { maxFlowPercent[i] = Math.Min(maxWeights[i], responses[i].flowPercent); preferredFlowPercent[i] = Math.Min(normalWeights[i], maxFlowPercent[i]) * desiredPercent; currentSum += preferredFlowPercent[i]; percentToReturn += preferredFlowPercent[i]; sumOfMaxFlow += maxFlowPercent[i]; } } //TODO: Find better way to handle this. The 0.000000001 is there to handle rounding errors, when the sum really is the same number, but it rounds to slightly less... if (currentSum < desiredPercent - 0.000000001 && sumOfMaxFlow > 0.0) //sum > 0.0 helps us avoid a divide by 0 below. Also, if we can't push anything, then 0 is the right answer { percentToReturn = 0; for (int i = 0; i < nodes.Length; i++) { double trueFlowPercent; if (sumOfMaxFlow == 0.0) trueFlowPercent = 0.0; else trueFlowPercent = desiredPercent * (maxFlowPercent[i] / sumOfMaxFlow); trueFlowPercent = Math.Min(trueFlowPercent, maxWeights[i]); //Probably don't need this one, since the one right below is better. trueFlowPercent = Math.Min(trueFlowPercent, maxFlowPercent[i]); flowPercentageSolutions[baseData.flowDriver.name][i] = trueFlowPercent; percentToReturn += trueFlowPercent; } } else { for (int i = 0; i < nodes.Length; i++) { double trueFlowPercent = preferredFlowPercent[i]; //It all works out with preferred values. flowPercentageSolutions[baseData.flowDriver.name][i] = trueFlowPercent; } } //Need to normalize the flowPercenageSolutions, so that they sum to 1.0, so that when we set values, we are spliting 100 % of the smaller portion that is coming down to us(and don't double-limit things). if (percentToReturn != 0.0) { for (int i = 0; i < nodes.Length; i++) flowPercentageSolutions[baseData.flowDriver.name][i] /= percentToReturn; } FlowResponseData ret = new FlowResponseData(); ret.flowPercent = percentToReturn; ret.flowVolume = percentToReturn * baseData.desiredFlowVolume; ret.backPressure = maxBackPressure; setPressures(baseData.pressure, ret.backPressure, pressurePercent, isDeliverySide); return ret; }
public override SettingResponseData setSourceValues(FlowCalculationData baseData, FlowComponent caller, double flowVolume, bool lastTime) { if (!hasMultipleDeliveryComponents) return setFlowValues(baseData, caller, flowVolume, sourceComponents, false, lastTime); else return setCombiningFlowValues(baseData, caller, flowVolume, sourceComponents, false, lastTime); }
public override void setDeliveryValues(FlowCalculationData baseData, FlowComponent caller, double flowVolume, bool lastTime) { if (hasMultipleDeliveryComponents) setFlowValues(baseData, caller, flowVolume, deliveryComponents, true, lastTime); else setCombiningFlowValues(baseData, caller, flowVolume, deliveryComponents, true, lastTime); }
public override FlowResponseData getSourcePossibleValues(FlowCalculationData baseData, FlowComponent caller, double flowPercent, double pressurePercent) { if (!hasMultipleDeliveryComponents) return calculateSplittingFunctionality(baseData, flowPercent, sourceComponents, false, pressurePercent); else return calculateCombiningFunctionality(baseData, caller, flowPercent, deliveryComponents, false, pressurePercent); }
public override void exploreSourceGraph(FlowCalculationData baseData, FlowComponent caller) { sourceComponent.exploreSourceGraph(baseData, this); }
public abstract void setDeliveryValues(FlowCalculationData baseData, FlowComponent caller, double flowVolume, bool lastTime);
public abstract FlowResponseData getSourcePossibleValues(FlowCalculationData baseData, FlowComponent caller, double flowPercent,double pressurePercent);
private SettingResponseData setFlowValues(FlowCalculationData baseData, FlowComponent caller, double flowVolume, FlowComponent[] nodes, bool isDeliverySide, bool lastTime) { if(isDeliverySide) { for (int i = 0; i < nodes.Length; i++) { nodes[i].setDeliveryValues(baseData, this, flowPercentageSolutions[baseData.flowDriver.name][i] * flowVolume, lastTime); } finalFlow = flowVolume; currentFluidTypeMap = baseData.fluidTypeMap; //On the delivery side, the mixture comes from passed in arguments inletTemperature = baseData.temperature; outletTemperature = baseData.temperature; return null; } else { SettingResponseData[] responses = new SettingResponseData[nodes.Length]; double[] volumes = new double[nodes.Length]; bool hasNull = false; for (int attempt = 0; attempt < 2; attempt++) { hasNull = false; for (int i = 0; i < nodes.Length; i++) { volumes[i] = flowPercentageSolutions[baseData.flowDriver.name][i] * flowVolume; if(responses[i] == null) responses[i] = nodes[i].setSourceValues(baseData, this, volumes[i], lastTime); if (responses[i] == null) hasNull = true; } if (!hasNull) break; } if (hasNull) return null; finalFlow = flowVolume; SettingResponseData ret = PhysTools.mixFluidPercentsAndTemperatures(responses, volumes); ret.flowVolume = flowVolume; //ret.fluidTypeMap = PhysTools.mixFluids(responses, volumes); currentFluidTypeMap = ret.fluidTypeMap; //On source side, the mixture comes from the return values inletTemperature = ret.temperature; outletTemperature = ret.temperature; return ret; } }
public override FlowResponseData getSourcePossibleValues(FlowCalculationData baseData, FlowComponent caller, double flowPercent, double pressurePercent) { FlowResponseData ret = new FlowResponseData(); if(currentVolume > 0.0) { ret.flowPercent = flowPercent; //Allow everything that they are asking for, since we don't do restrictions inside the tank. } else { ret.flowPercent = 0.0f; } ret.flowVolume = flowPercent * baseData.desiredFlowVolume; ret.backPressure = tankPressure; ret.fluidTypeMap = currentFluidTypeMap; outletPressure = ret.backPressure; return ret; }
public override void exploreDeliveryGraph(FlowCalculationData baseData, FlowComponent caller) { for (int i = 0; i < deliveryComponents.Length; i++) { deliveryComponents[i].exploreDeliveryGraph(baseData, this); } if (!hasMultipleDeliveryComponents) //Do the following if we are combining, so we know which combining imputs actually apply to given pump { if (!indexesUsedByPump.ContainsKey(baseData.flowDriver.name)) indexesUsedByPump[baseData.flowDriver.name] = new bool[sourceComponents.Length]; indexesUsedByPump[baseData.flowDriver.name][indexByName[caller.name]] = true; } }
public override SettingResponseData setSourceValues(FlowCalculationData baseData, FlowComponent caller, double flowVolume, bool lastTime) { SettingResponseData ret = new SettingResponseData(); finalFlow -= flowVolume; if(lastTime) { currentVolume -= flowVolume * PhysTools.timeStep; } ret.flowVolume = flowVolume; ret.fluidTypeMap = currentFluidTypeMap; ret.temperature = currentTemperature; inletTemperature = currentTemperature; outletTemperature = currentTemperature; return ret; }
public override SettingResponseData setSourceValues(FlowCalculationData baseData, FlowComponent caller, double flowVolume, bool lastTime) { finalFlow = flowVolume; SettingResponseData ret = sourceComponent.setSourceValues(baseData, this, flowVolume, lastTime); if (ret != null) { currentFluidTypeMap = ret.fluidTypeMap; //On source side, the mixture comes from the return values inletTemperature = ret.temperature; outletTemperature = ret.temperature; } return ret; }
private SettingResponseData setCombiningFlowValues(FlowCalculationData baseData, FlowComponent caller, double flowVolume, FlowComponent[] nodes, bool isDeliverySide, bool lastTime) { int index = indexByName[caller.name]; setCombiningVolumeMap[index] = flowVolume; double volumeSum = 0.0; for (int i = 0; i < setCombiningVolumeMap.Length; i++) { if (setCombiningVolumeMap[i] < 0.0) return null; //We haven't seen all of the inputs to combine them... else volumeSum += setCombiningVolumeMap[i]; } finalFlow = volumeSum; if (isDeliverySide) { for (int i = 0; i < nodes.Length; i++) { nodes[i].setDeliveryValues(baseData, this, volumeSum, lastTime); } currentFluidTypeMap = baseData.fluidTypeMap; //On the delivery side, the mixture comes from passed in arguments inletTemperature = baseData.temperature; outletTemperature = baseData.temperature; return null; } else { if (nodes.Length != 1) throw new Exception("Source combiners can only have 1 node"); if (setterDownstreamValue == null) setterDownstreamValue = nodes[0].setSourceValues(baseData, this, volumeSum, lastTime); SettingResponseData ret = new SettingResponseData(); ret.flowVolume = volumeSum; ret.fluidTypeMap = setterDownstreamValue.fluidTypeMap; ret.temperature = setterDownstreamValue.temperature; currentFluidTypeMap = ret.fluidTypeMap; //On source side, the mixture comes from the return values inletTemperature = ret.temperature; outletTemperature = ret.temperature; return ret; } }
private bool attemptSolve(FlowDriver p, int attempt) { double flowModifier = flowDriverModifiers[p.name].flowPercent; FlowCalculationData baseData = new FlowCalculationData(p, angerMap, attempt); FlowResponseData sourceAbility = p.getFlowDriverSourcePossibleValues(baseData, flowDriverModifiers[p.name]); baseData.fluidTypeMap = sourceAbility.fluidTypeMap; //Pass the mixture stuff from source to delivery FlowResponseData deliveryAbility = p.getFlowDriverDeliveryPossibleValues(baseData, flowDriverModifiers[p.name]); if (flowDriverModifiers[p.name].updateStateRequiresNewSolution(sourceAbility, deliveryAbility)) return false; return true; }