Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,25 @@ public void run(UUID id) {
* @return a {@link neqsim.process.equipment.stream.Stream} object
*/
public StreamInterface getGasOutStream() {
return new Stream("", mixedStream.getThermoSystem().phaseToSystem(0));
SystemInterface thermoSys = mixedStream.getThermoSystem();
// Find gas phase by type, fallback to phase 0
for (int i = 0; i < thermoSys.getNumberOfPhases(); i++) {
if (thermoSys.getPhase(i).getType() == neqsim.thermo.phase.PhaseType.GAS) {
try {
return new Stream("", thermoSys.phaseToSystem(i));
} catch (Exception e) {
logger.warn("Failed to extract gas phase " + i + ", trying next", e);
}
}
}
// If no gas phase found, return phase 0 (original behavior)
try {
return new Stream("", thermoSys.phaseToSystem(0));
} catch (Exception e) {
logger.error("Failed to extract any gas phase", e);
// Return a stream with the full system as fallback
return new Stream("", thermoSys.clone());
}
}

/**
Expand All @@ -217,7 +235,35 @@ public StreamInterface getGasOutStream() {
* @return a {@link neqsim.process.equipment.stream.Stream} object
*/
public StreamInterface getLiquidOutStream() {
return new Stream("", mixedStream.getThermoSystem().phaseToSystem(1));
SystemInterface thermoSys = mixedStream.getThermoSystem();
// Find liquid phase by type
for (int i = 0; i < thermoSys.getNumberOfPhases(); i++) {
if (thermoSys.getPhase(i).getType() == neqsim.thermo.phase.PhaseType.LIQUID
|| thermoSys.getPhase(i).getType() == neqsim.thermo.phase.PhaseType.OIL
|| thermoSys.getPhase(i).getType() == neqsim.thermo.phase.PhaseType.AQUEOUS) {
try {
return new Stream("", thermoSys.phaseToSystem(i));
} catch (Exception e) {
logger.warn("Failed to extract liquid phase " + i + ", trying next", e);
}
}
}
// If no liquid phase found, return phase 1 if it exists, otherwise phase 0
if (thermoSys.getNumberOfPhases() > 1) {
try {
return new Stream("", thermoSys.phaseToSystem(1));
} catch (Exception e) {
logger.warn("Failed to extract phase 1", e);
}
}
// Only one phase exists or extraction failed - return phase 0 as fallback
try {
return new Stream("", thermoSys.phaseToSystem(0));
} catch (Exception e) {
logger.error("Failed to extract any liquid phase", e);
// Return a stream with the full system as fallback
return new Stream("", thermoSys.clone());
}
}

/** {@inheritDoc} */
Expand Down
89 changes: 77 additions & 12 deletions src/main/java/neqsim/process/equipment/mixer/Mixer.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import javax.swing.JDialog;
Expand Down Expand Up @@ -48,6 +50,10 @@ public class Mixer extends ProcessEquipmentBaseClass implements MixerInterface {

private boolean doMultiPhaseCheck = true;

/** Cached values for needRecalculation check. */
private double lastTotalEnthalpy = Double.NaN;
private double lastTotalFlow = Double.NaN;

/**
* <p>
* Setter for the field <code>doMultiPhaseCheck</code>.
Expand Down Expand Up @@ -137,7 +143,6 @@ public StreamInterface getStream(int i) {
* </p>
*/
public void mixStream() {
int index = 0;
lowestPressure = mixedStream.getThermoSystem().getPhase(0).getPressure();
boolean hasAddedNewComponent = false;
for (int k = 1; k < streams.size(); k++) {
Expand All @@ -149,6 +154,15 @@ public void mixStream() {
// streams.get(k).getThermoSystem().getPhase(0).setPressure(lowestPressure);
}

// Build component name -> index map for O(1) lookup instead of O(n) inner loop
Map<String, Integer> componentIndexMap = new HashMap<>();
for (int p = 0; p < mixedStream.getThermoSystem().getPhase(0).getNumberOfComponents(); p++) {
String name = mixedStream.getThermoSystem().getPhase(0).getComponent(p).getName();
int compNum =
streams.get(0).getThermoSystem().getPhase(0).getComponent(p).getComponentNumber();
componentIndexMap.put(name, compNum);
}

// Process ALL streams starting from k=1 (k=0 is already cloned into mixedStream)
// but ensure first stream's components are also explicitly added if needed
for (int k = 1; k < streams.size(); k++) {
Expand All @@ -159,28 +173,24 @@ public void mixStream() {

for (int i = 0; i < streams.get(k).getThermoSystem().getPhase(0)
.getNumberOfComponents(); i++) {
boolean gotComponent = false;
String componentName =
streams.get(k).getThermoSystem().getPhase(0).getComponent(i).getName();

double moles =
streams.get(k).getThermoSystem().getPhase(0).getComponent(i).getNumberOfmoles();

for (int p = 0; p < mixedStream.getThermoSystem().getPhase(0)
.getNumberOfComponents(); p++) {
if (mixedStream.getThermoSystem().getPhase(0).getComponent(p).getName()
.equals(componentName)) {
gotComponent = true;
index =
streams.get(0).getThermoSystem().getPhase(0).getComponent(p).getComponentNumber();
}
}
// O(1) lookup using HashMap instead of O(n) loop
Integer index = componentIndexMap.get(componentName);

if (gotComponent) {
if (index != null) {
mixedStream.getThermoSystem().addComponent(index, moles);
} else {
hasAddedNewComponent = true;
mixedStream.getThermoSystem().addComponent(componentName, moles);
// Add to map for future lookups within this mixing operation
int newIndex = mixedStream.getThermoSystem().getPhase(0).getComponent(componentName)
.getComponentNumber();
componentIndexMap.put(componentName, newIndex);
}
}
}
Expand Down Expand Up @@ -230,6 +240,49 @@ public StreamInterface getOutletStream() {
return mixedStream;
}

/** {@inheritDoc} */
@Override
public boolean needRecalculation() {
// Calculate current total enthalpy and flow from all input streams
double totalEnthalpy = 0.0;
double totalFlow = 0.0;
for (int k = 0; k < streams.size(); k++) {
if (streams.get(k).getFlowRate("kg/hr") > getMinimumFlow()) {
totalEnthalpy += streams.get(k).getThermoSystem().getEnthalpy();
totalFlow += streams.get(k).getFlowRate("kg/hr");
}
}

// Check if values have changed significantly
if (!Double.isNaN(lastTotalEnthalpy) && !Double.isNaN(lastTotalFlow)) {
// Handle zero/near-zero cases to avoid division by zero
if (totalFlow < 1e-10 && lastTotalFlow < 1e-10) {
return false; // Both are essentially zero - no recalc needed
}
if (totalFlow > 0 && lastTotalFlow > 0) {
double flowChange = Math.abs((totalFlow - lastTotalFlow) / lastTotalFlow);
if (flowChange >= 1e-6) {
return true; // Flow changed significantly
}
} else if (Math.abs(totalFlow - lastTotalFlow) > 1e-10) {
return true; // One is zero, other isn't
}

// Check enthalpy change (only if we have flow)
if (totalFlow > 0 && Math.abs(lastTotalEnthalpy) > 1e-10) {
double enthalpyChange = Math.abs((totalEnthalpy - lastTotalEnthalpy) / lastTotalEnthalpy);
if (enthalpyChange >= 1e-6) {
return true; // Enthalpy changed significantly
}
} else if (Math.abs(totalEnthalpy - lastTotalEnthalpy) > 1e-10) {
return true; // Enthalpy changed from/to zero
}

return false; // No significant changes
}
return true; // First run or invalid cached values
}

/** {@inheritDoc} */
@Override
public void run(UUID id) {
Expand All @@ -254,6 +307,8 @@ public void run(UUID id) {
}
mixedStream.setThermoSystem(thermoSystem2);
isActive(false);
lastTotalEnthalpy = 0.0;
lastTotalFlow = 0.0;
setCalculationIdentifier(id);
return;
}
Expand Down Expand Up @@ -324,6 +379,16 @@ public void run(UUID id) {
mixedStream.getThermoSystem().setMultiPhaseCheck(true);
}

// Update cached values for needRecalculation check
lastTotalEnthalpy = 0.0;
lastTotalFlow = 0.0;
for (int k = 0; k < streams.size(); k++) {
if (streams.get(k).getFlowRate("kg/hr") > getMinimumFlow()) {
lastTotalEnthalpy += streams.get(k).getThermoSystem().getEnthalpy();
lastTotalFlow += streams.get(k).getFlowRate("kg/hr");
}
}

setCalculationIdentifier(id);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,10 @@ public void setEntrainment(double val, String specType, String specifiedStream,
/** {@inheritDoc} */
@Override
public void run(UUID id) {
inletStreamMixer.run(id);
// Check if inlet mixer needs recalculation before running it
if (inletStreamMixer.needRecalculation()) {
inletStreamMixer.run(id);
}
double enthalpy = inletStreamMixer.getOutletStream().getFluid().getEnthalpy();
double flow = inletStreamMixer.getOutletStream().getFlowRate("kg/hr");
double pres = inletStreamMixer.getOutletStream().getPressure();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ public void run(UUID id) {
return;
}

// Initialize properties only once on the cloned system (needed for phase detection and
// enthalpy)
thermoSystem.initProperties();

if (thermoSystem.hasPhaseType(PhaseType.GAS) && thermoSystem.getVolumeFraction(0) > 0.5) {
Expand All @@ -254,11 +256,11 @@ public void run(UUID id) {
calcKv();
valveKvSet = true;
}
inStream.getThermoSystem().initProperties();
// Use already-initialized thermoSystem for enthalpy instead of calling initProperties again on
// inlet
double enthalpy = thermoSystem.getEnthalpy();

double outPres = getOutletStream().getThermoSystem().getPressure();
double molarFlowStart = getInletStream().getThermoSystem().getFlowRate("mole/sec");
// first estimate of flow from current outlet pressure
// Calculate molar flow rate for gas directly here (without calling
// calculateMolarFlowRateGas)
Expand Down Expand Up @@ -296,8 +298,16 @@ public void run(UUID id) {
}

ThermodynamicOperations thermoOps = new ThermodynamicOperations(thermoSystem);
if (isIsoThermal() || Math.abs(pressure - inStream.getThermoSystem().getPressure()) < 1e-6
|| thermoSystem.getTotalNumberOfMoles() < 1e-12 || pressure == 0) {

// Calculate pressure ratio to determine if TPflash can be used as approximation
double inletPres = inStream.getThermoSystem().getPressure();
double pressureRatio = Math.abs(pressure - inletPres) / inletPres;

// Use TPflash for isothermal operation, negligible pressure change, or very small flow
// For small pressure changes (<0.5%), the temperature change via Joule-Thomson is minimal
// and TPflash provides a good approximation with much better performance
if (isIsoThermal() || pressureRatio < 0.005 || thermoSystem.getTotalNumberOfMoles() < 1e-12
|| pressure == 0) {
thermoSystem.setPressure(outPres, pressureUnit);
thermoOps.TPflash();
} else {
Expand Down
Loading
Loading