From 6cd2f4c8cbfd322d0b0a4c69fe6aa08c310bb1c3 Mon Sep 17 00:00:00 2001
From: Even Solbraa <41290109+EvenSol@users.noreply.github.com>
Date: Sat, 4 Jan 2025 11:51:44 +0100
Subject: [PATCH] add multi stream HX (#1230)
* add multi stream HX
* update
* update
* update model
---
.../MultiStreamHeatExchanger.java | 655 ++++++++++++++++++
.../MultiStreamHeatExchangerInterface.java | 296 ++++++++
.../MultiStreamHeatExchangerTest.java | 69 ++
3 files changed, 1020 insertions(+)
create mode 100644 src/main/java/neqsim/process/equipment/heatexchanger/MultiStreamHeatExchanger.java
create mode 100644 src/main/java/neqsim/process/equipment/heatexchanger/MultiStreamHeatExchangerInterface.java
create mode 100644 src/test/java/neqsim/process/equipment/heatexchanger/MultiStreamHeatExchangerTest.java
diff --git a/src/main/java/neqsim/process/equipment/heatexchanger/MultiStreamHeatExchanger.java b/src/main/java/neqsim/process/equipment/heatexchanger/MultiStreamHeatExchanger.java
new file mode 100644
index 0000000000..cb63acee2c
--- /dev/null
+++ b/src/main/java/neqsim/process/equipment/heatexchanger/MultiStreamHeatExchanger.java
@@ -0,0 +1,655 @@
+/*
+ * MultiStreamHeatExchanger.java
+ *
+ * Created on [Date]
+ */
+
+package neqsim.process.equipment.heatexchanger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import neqsim.process.conditionmonitor.ConditionMonitorSpecifications;
+import neqsim.process.equipment.ProcessEquipmentInterface;
+import neqsim.process.equipment.stream.StreamInterface;
+import neqsim.thermo.system.SystemInterface;
+import neqsim.thermodynamicoperations.ThermodynamicOperations;
+import neqsim.util.ExcludeFromJacocoGeneratedReport;
+
+/**
+ *
+ * MultiStreamHeatExchanger class.
+ *
+ *
+ * Extends the Heater class to support multiple input and output streams, enabling the simulation of
+ * complex heat exchange processes such as those found in LNG heat exchangers.
+ *
+ * @author [Your Name]
+ * @version 1.0
+ */
+public class MultiStreamHeatExchanger extends Heater implements MultiStreamHeatExchangerInterface {
+ private static final long serialVersionUID = 1001;
+ static Logger logger = LogManager.getLogger(MultiStreamHeatExchanger.class);
+
+ private boolean setTemperature = false;
+ private List outStreams = new ArrayList<>();
+ private List inStreams = new ArrayList<>();
+ private SystemInterface system;
+ private double NTU;
+ protected double temperatureOut = 0;
+
+ protected double dT = 0.0;
+ private double temperatureApproach = 0.0;
+
+
+ private double UAvalue = 500.0; // Overall heat transfer coefficient times area
+ private double duty = 0.0;
+ private double hotColdDutyBalance = 1.0;
+ private boolean firstTime = true;
+ private double guessOutTemperature = 273.15 + 130.0; // Default guess in K
+ private String guessOutTemperatureUnit = "K";
+ private int outStreamSpecificationNumber = 0;
+ private double thermalEffectiveness = 0.0;
+ private String flowArrangement = "counterflow"; // Default arrangement
+ private boolean useDeltaT = false;
+ private double deltaT = 1.0;
+
+ /**
+ * Constructor for MultiStreamHeatExchanger.
+ *
+ * @param name Name of the heat exchanger
+ */
+ public MultiStreamHeatExchanger(String name) {
+ super(name);
+ }
+
+ /**
+ * Constructor for MultiStreamHeatExchanger with initial input streams.
+ *
+ * @param name Name of the heat exchanger
+ * @param inStreams Initial list of input streams
+ */
+ public MultiStreamHeatExchanger(String name, List inStreams) {
+ this(name);
+ this.inStreams.addAll(inStreams);
+ // Initialize outStreams as clones of inStreams
+ for (StreamInterface inStream : inStreams) {
+ StreamInterface outStream = inStream.clone();
+ outStream.setName(name + "_Sout" + (outStreams.size() + 1));
+ this.outStreams.add(outStream);
+ }
+ setName(name);
+ }
+
+ /**
+ * Adds an inlet stream to the heat exchanger.
+ *
+ * @param inStream Input stream to add
+ */
+ public void addInStream(StreamInterface inStream) {
+ this.inStreams.add(inStream);
+ StreamInterface outStream = inStream.clone();
+ outStream.setName(getName() + "_Sout" + (outStreams.size() + 1));
+ this.outStreams.add(outStream);
+ }
+
+ /**
+ * Sets the feed stream at a specific index.
+ *
+ * @param index Index of the stream to set
+ * @param inStream Input stream to set
+ */
+ public void setFeedStream(int index, StreamInterface inStream) {
+ if (index < inStreams.size()) {
+ this.inStreams.set(index, inStream);
+ this.outStreams.set(index, inStream.clone());
+ setName(getName());
+ } else {
+ throw new IndexOutOfBoundsException("Stream index out of bounds.");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setName(String name) {
+ super.setName(name);
+ for (int i = 0; i < outStreams.size(); i++) {
+ outStreams.get(i).setName(name + "_Sout" + (i + 1));
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setdT(double dT) {
+ this.dT = dT;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public StreamInterface getOutStream(int i) {
+ return outStreams.get(i);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public StreamInterface getInStream(int i) {
+ return inStreams.get(i);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setOutTemperature(double temperature) {
+ this.temperatureOut = temperature;
+ }
+
+ /**
+ * Gets the output temperature of a specific stream.
+ *
+ * @param i Index of the stream
+ * @return Temperature of the output stream
+ */
+ public double getOutTemperature(int i) {
+ return outStreams.get(i).getThermoSystem().getTemperature();
+ }
+
+ /**
+ * Gets the input temperature of a specific stream.
+ *
+ * @param i Index of the stream
+ * @return Temperature of the input stream
+ */
+ public double getInTemperature(int i) {
+ return inStreams.get(i).getThermoSystem().getTemperature();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getDuty() {
+ return duty;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @ExcludeFromJacocoGeneratedReport
+ public void displayResult() {
+ for (StreamInterface outStream : outStreams) {
+ outStream.displayResult();
+ }
+ }
+
+ /**
+ * Gets the overall UA value.
+ *
+ * @return UA value
+ */
+ public double getUAvalue() {
+ return UAvalue;
+ }
+
+ /**
+ * Sets the overall UA value.
+ *
+ * @param UAvalue UA value to set
+ */
+ public void setUAvalue(double UAvalue) {
+ this.UAvalue = UAvalue;
+ }
+
+ /**
+ * Gets the guessed outlet temperature.
+ *
+ * @return Guessed outlet temperature
+ */
+ public double getGuessOutTemperature() {
+ return guessOutTemperature;
+ }
+
+ /**
+ * Sets the guessed outlet temperature in Kelvin.
+ *
+ * @param guessOutTemperature Guessed outlet temperature
+ */
+ public void setGuessOutTemperature(double guessOutTemperature) {
+ this.guessOutTemperature = guessOutTemperature;
+ this.guessOutTemperatureUnit = "K";
+ }
+
+ /**
+ * Sets the guessed outlet temperature with specified unit.
+ *
+ * @param guessOutTemperature Guessed outlet temperature
+ * @param unit Unit of the temperature
+ */
+ public void setGuessOutTemperature(double guessOutTemperature, String unit) {
+ this.guessOutTemperature = guessOutTemperature;
+ this.guessOutTemperatureUnit = unit;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getEntropyProduction(String unit) {
+ double entropyProduction = 0.0;
+
+ for (int i = 0; i < inStreams.size(); i++) {
+ UUID id = UUID.randomUUID();
+ inStreams.get(i).run(id);
+ inStreams.get(i).getFluid().init(3);
+ outStreams.get(i).run(id);
+ outStreams.get(i).getFluid().init(3);
+ entropyProduction += outStreams.get(i).getThermoSystem().getEntropy(unit)
+ - inStreams.get(i).getThermoSystem().getEntropy(unit);
+ }
+
+ // Additional entropy production due to heat transfer
+ // Assuming the first stream is the hot stream and the last is cold
+ if (inStreams.size() >= 2) {
+ int hotStream = 0;
+ int coldStream = inStreams.size() - 1;
+ double heatTransferEntropyProd =
+ Math.abs(getDuty()) * (1.0 / inStreams.get(coldStream).getTemperature()
+ - 1.0 / inStreams.get(hotStream).getTemperature());
+ entropyProduction += heatTransferEntropyProd;
+ }
+
+ return entropyProduction;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getMassBalance(String unit) {
+ double massBalance = 0.0;
+
+ for (int i = 0; i < inStreams.size(); i++) {
+ inStreams.get(i).run();
+ inStreams.get(i).getFluid().init(3);
+ outStreams.get(i).run();
+ outStreams.get(i).getFluid().init(3);
+ massBalance += outStreams.get(i).getThermoSystem().getFlowRate(unit)
+ - inStreams.get(i).getThermoSystem().getFlowRate(unit);
+ }
+ return massBalance;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void runConditionAnalysis(ProcessEquipmentInterface refExchanger) {
+ double heatBalanceError = 0.0;
+ conditionAnalysisMessage += getName() + " condition analysis started/";
+ MultiStreamHeatExchanger refEx = (MultiStreamHeatExchanger) refExchanger;
+
+ for (int i = 0; i < inStreams.size(); i++) {
+ inStreams.get(i).getFluid().initProperties();
+ outStreams.get(i).getFluid().initProperties();
+ heatBalanceError += outStreams.get(i).getThermoSystem().getEnthalpy()
+ - inStreams.get(i).getThermoSystem().getEnthalpy();
+
+ if (Math.abs(refEx.getInStream(i).getTemperature("C")
+ - getInStream(i).getTemperature("C")) > ConditionMonitorSpecifications.HXmaxDeltaT) {
+ conditionAnalysisMessage += ConditionMonitorSpecifications.HXmaxDeltaT_ErrorMsg;
+ } else if (Math.abs(refEx.getOutStream(i).getTemperature("C")
+ - getOutStream(i).getTemperature("C")) > ConditionMonitorSpecifications.HXmaxDeltaT) {
+ conditionAnalysisMessage += ConditionMonitorSpecifications.HXmaxDeltaT_ErrorMsg;
+ }
+ }
+
+ heatBalanceError = heatBalanceError / (outStreams.get(0).getThermoSystem().getEnthalpy()
+ - inStreams.get(0).getThermoSystem().getEnthalpy()) * 100.0;
+
+ if (Math.abs(heatBalanceError) > 10.0) {
+ String error = "Heat balance not fulfilled. Error: " + heatBalanceError + " ";
+ conditionAnalysisMessage += error;
+ } else {
+ String message = "Heat balance ok. Enthalpy balance deviation: " + heatBalanceError + " %";
+ conditionAnalysisMessage += message;
+ }
+
+ conditionAnalysisMessage += getName() + "/analysis ended/";
+
+ // Calculate thermal effectiveness and duty
+ double totalDuty = 0.0;
+ for (int i = 0; i < inStreams.size(); i++) {
+ double dutyStream = Math.abs(outStreams.get(i).getThermoSystem().getEnthalpy()
+ - inStreams.get(i).getThermoSystem().getEnthalpy());
+ totalDuty += dutyStream;
+ }
+
+ double referenceDuty = Math.abs(((MultiStreamHeatExchanger) refExchanger).getDuty());
+ thermalEffectiveness = ((MultiStreamHeatExchanger) refExchanger).getThermalEffectiveness()
+ * (totalDuty) / referenceDuty;
+
+ // Optionally, calculate duty balance among streams
+ // This can be customized based on specific requirements
+ }
+
+ /**
+ * Runs condition analysis by comparing the exchanger with itself.
+ */
+ public void runConditionAnalysis() {
+ runConditionAnalysis(this);
+ }
+
+ /**
+ * Gets the thermal effectiveness of the heat exchanger.
+ *
+ * @return Thermal effectiveness
+ */
+ public double getThermalEffectiveness() {
+ return thermalEffectiveness;
+ }
+
+ /**
+ * Sets the thermal effectiveness of the heat exchanger.
+ *
+ * @param thermalEffectiveness Thermal effectiveness to set
+ */
+ public void setThermalEffectiveness(double thermalEffectiveness) {
+ this.thermalEffectiveness = thermalEffectiveness;
+ }
+
+ /**
+ * Gets the flow arrangement of the heat exchanger.
+ *
+ * @return Flow arrangement
+ */
+ public String getFlowArrangement() {
+ return flowArrangement;
+ }
+
+ /**
+ * Sets the flow arrangement of the heat exchanger.
+ *
+ * @param flowArrangement Name of the flow arrangement
+ */
+ public void setFlowArrangement(String flowArrangement) {
+ this.flowArrangement = flowArrangement;
+ }
+
+ /**
+ * Calculates the thermal effectiveness based on NTU and capacity ratio.
+ *
+ * @param NTU NTU value
+ * @param Cr Capacity ratio (Cmin/Cmax)
+ * @return Thermal effectiveness
+ */
+ public double calcThermalEffectiveness(double NTU, double Cr) {
+ switch (flowArrangement.toLowerCase()) {
+ case "counterflow":
+ if (Cr == 1.0) {
+ return NTU / (1.0 + NTU);
+ } else {
+ return (1.0 - Math.exp(-NTU * (1 - Cr))) / (1.0 - Cr * Math.exp(-NTU * (1 - Cr)));
+ }
+ case "parallelflow":
+ return (1.0 - Math.exp(-NTU * (1 + Cr))) / (1.0 + Cr);
+ case "crossflow":
+ // Simplified model for crossflow; more complex models can be implemented
+ return 1 - Math.exp(-NTU * Math.pow(1 + Cr, 0.22));
+ default:
+ // Default to counterflow if arrangement is unrecognized
+ if (Cr == 1.0) {
+ return NTU / (1.0 + NTU);
+ } else {
+ return (1.0 - Math.exp(-NTU * (1 - Cr))) / (1.0 - Cr * Math.exp(-NTU * (1 - Cr)));
+ }
+ }
+ }
+
+ /**
+ * Gets the hot and cold duty balance.
+ *
+ * @return Hot and cold duty balance
+ */
+ public double getHotColdDutyBalance() {
+ return hotColdDutyBalance;
+ }
+
+ /**
+ * Sets the hot and cold duty balance.
+ *
+ * @param hotColdDutyBalance Hot and cold duty balance to set
+ */
+ public void setHotColdDutyBalance(double hotColdDutyBalance) {
+ this.hotColdDutyBalance = hotColdDutyBalance;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toJson() {
+ // return new GsonBuilder().serializeSpecialFloatingPointValues().create()
+ // .toJson(new HXResponse(this));
+ return super.toJson();
+ }
+
+ /**
+ * Sets whether to use delta T for the calculations.
+ *
+ * @param useDeltaT Boolean flag to use delta T
+ */
+ public void setUseDeltaT(boolean useDeltaT) {
+ this.useDeltaT = useDeltaT;
+ }
+
+ /**
+ * Gets the delta T value.
+ *
+ * @return Delta T
+ */
+ public double getDeltaT() {
+ return deltaT;
+ }
+
+ /**
+ * Sets the delta T value and enables its usage.
+ *
+ * @param deltaT Delta T to set
+ */
+ public void setDeltaT(double deltaT) {
+ this.useDeltaT = true;
+ this.deltaT = deltaT;
+ }
+
+ /**
+ * Runs the heat exchanger simulation considering multiple streams.
+ *
+ * @param id Unique identifier for the run
+ */
+ @Override
+ public void run(UUID id) {
+ if (firstTime) {
+ firstTime = false;
+
+ // 1. Identify the hottest and coldest inlet streams
+ double hottestTemperature = Double.NEGATIVE_INFINITY;
+ double coldestTemperature = Double.POSITIVE_INFINITY;
+ int hottestIndex = -1;
+ int coldestIndex = -1;
+
+ for (int i = 0; i < inStreams.size(); i++) {
+ StreamInterface inStream = inStreams.get(i);
+ // Ensure the inlet stream is run to get the latest temperature
+ inStream.run();
+ double currentTemp = inStream.getThermoSystem().getTemperature("K");
+
+ if (currentTemp > hottestTemperature) {
+ hottestTemperature = currentTemp;
+ hottestIndex = i;
+ }
+
+ if (currentTemp < coldestTemperature) {
+ coldestTemperature = currentTemp;
+ coldestIndex = i;
+ }
+ }
+
+ // Check if valid indices were found
+ if (hottestIndex == -1 || coldestIndex == -1) {
+ throw new IllegalStateException("Unable to determine hottest or coldest inlet streams.");
+ }
+
+ // 2. Set the outlet temperatures accordingly
+ for (int i = 0; i < outStreams.size(); i++) {
+ StreamInterface outStream = outStreams.get(i);
+ SystemInterface systemOut = inStreams.get(i).getThermoSystem().clone();
+ outStream.setThermoSystem(systemOut);
+
+ if (i == hottestIndex) {
+ // Set the outlet temperature of the hottest inlet stream to the coldest inlet temperature
+ outStream.getThermoSystem().setTemperature(coldestTemperature + temperatureApproach, "K");
+ } else if (i == coldestIndex) {
+ // Set the outlet temperature of the coldest inlet stream to the hottest inlet temperature
+ outStream.getThermoSystem().setTemperature(hottestTemperature - temperatureApproach, "K");
+ } else {
+ // Set the outlet temperature of other streams to the hottest inlet temperature
+ outStream.getThermoSystem().setTemperature(hottestTemperature - temperatureApproach, "K");
+ }
+
+ // Run the outlet stream with the given ID
+ outStream.run(id);
+ }
+
+ // Finalize the setup
+ run();
+ return;
+ }
+
+ else {
+ // Run all input and output streams to ensure they are up-to-date
+ for (StreamInterface inStream : inStreams) {
+ inStream.run(id);
+ }
+ for (StreamInterface outStream : outStreams) {
+ outStream.run(id);
+ }
+
+ // Identify heated and cooled streams
+ List heatedStreamIndices = new ArrayList<>();
+ List cooledStreamIndices = new ArrayList<>();
+ double totalHeatGained = 0.0; // Total Q for heated streams
+ double totalHeatLost = 0.0; // Total Q for cooled streams
+
+ for (int i = 0; i < inStreams.size(); i++) {
+ double enthalpyIn = inStreams.get(i).getThermoSystem().getEnthalpy();
+ double enthalpyOut = outStreams.get(i).getThermoSystem().getEnthalpy();
+ double deltaH = enthalpyOut - enthalpyIn;
+
+ if (deltaH > 0) {
+ // Stream is being heated
+ heatedStreamIndices.add(i);
+ totalHeatGained += deltaH;
+ } else if (deltaH < 0) {
+ // Stream is being cooled
+ cooledStreamIndices.add(i);
+ totalHeatLost += Math.abs(deltaH);
+ }
+ // Streams with deltaH == 0 are neither heated nor cooled
+ }
+
+ logger.debug(": Total Heat Gained = " + totalHeatGained + " J");
+ logger.debug(": Total Heat Lost = " + totalHeatLost + " J");
+
+ // Determine the limiting side
+ double limitingHeat;
+ boolean heatingIsLimiting;
+
+ if (totalHeatGained < totalHeatLost) {
+ limitingHeat = totalHeatGained;
+ heatingIsLimiting = true;
+ logger.debug("Limiting side: Heating");
+ } else {
+ limitingHeat = totalHeatLost;
+ heatingIsLimiting = false;
+ logger.debug("Limiting side: Cooling");
+ }
+
+ // Calculate scaling factors for each side
+ double scalingFactor = 1.0;
+
+ if (heatingIsLimiting) {
+ // Scale down the heat lost by cooled streams
+ scalingFactor = limitingHeat / totalHeatLost;
+ logger.debug("Scaling factor for cooled streams: " + scalingFactor);
+ } else {
+ // Scale down the heat gained by heated streams
+ scalingFactor = limitingHeat / totalHeatGained;
+ logger.debug("Scaling factor for heated streams: " + scalingFactor);
+ }
+
+ // Apply scaling factors to adjust outlet enthalpies
+ double maxTemperatureChange = 0.0;
+
+ for (int i : cooledStreamIndices) {
+ StreamInterface inStream = inStreams.get(i);
+ StreamInterface outStream = outStreams.get(i);
+
+ double enthalpyIn = inStream.getThermoSystem().getEnthalpy();
+ double targetDeltaH =
+ -(outStream.getThermoSystem().getEnthalpy() - enthalpyIn) * scalingFactor;
+
+ // Adjust the outlet enthalpy
+ double adjustedEnthalpyOut = enthalpyIn - (Math.abs(targetDeltaH));
+ ThermodynamicOperations ops = new ThermodynamicOperations(outStream.getThermoSystem());
+ ops.PHflash(adjustedEnthalpyOut);
+
+ // Calculate temperature change for convergence check
+ double oldTemp = outStream.getThermoSystem().getTemperature("K");
+ outStream.run(id); // Re-run to update temperature based on adjusted enthalpy
+ double newTemp = outStream.getThermoSystem().getTemperature("K");
+ double tempChange = Math.abs(newTemp - oldTemp);
+ if (tempChange > maxTemperatureChange) {
+ maxTemperatureChange = tempChange;
+ }
+
+ logger.debug("Adjusted cooled stream " + i + ": ΔH = " + targetDeltaH);
+ }
+
+ scalingFactor = 1.0;
+ for (int i : heatedStreamIndices) {
+ StreamInterface inStream = inStreams.get(i);
+ StreamInterface outStream = outStreams.get(i);
+
+ double enthalpyIn = inStream.getThermoSystem().getEnthalpy();
+ double targetDeltaH =
+ (outStream.getThermoSystem().getEnthalpy() - enthalpyIn) * scalingFactor;
+
+ // Adjust the outlet enthalpy
+ double adjustedEnthalpyOut = enthalpyIn + (Math.abs(targetDeltaH));
+ ThermodynamicOperations ops = new ThermodynamicOperations(outStream.getThermoSystem());
+ ops.PHflash(adjustedEnthalpyOut);
+
+ // Calculate temperature change for convergence check
+ double oldTemp = outStream.getThermoSystem().getTemperature("K");
+ outStream.run(id); // Re-run to update temperature based on adjusted enthalpy
+ double newTemp = outStream.getThermoSystem().getTemperature("K");
+ double tempChange = Math.abs(newTemp - oldTemp);
+ if (tempChange > maxTemperatureChange) {
+ maxTemperatureChange = tempChange;
+ }
+
+ logger.debug("Adjusted heated stream " + i + ": ΔH = " + targetDeltaH);
+ }
+ }
+ setCalculationIdentifier(id);
+ }
+
+
+ /**
+ * Runs the heat exchanger simulation using a specified stream approach.
+ *
+ * @param id Unique identifier for the run
+ */
+ public void runSpecifiedStream(UUID id) {
+ // Implementation similar to the two-stream case but generalized for multiple streams
+ // This method needs to be defined based on specific requirements
+ }
+
+
+ public double getTemperatureApproach() {
+ return temperatureApproach;
+ }
+
+ public void setTemperatureApproach(double temperatureApproach) {
+ this.temperatureApproach = temperatureApproach;
+ }
+}
diff --git a/src/main/java/neqsim/process/equipment/heatexchanger/MultiStreamHeatExchangerInterface.java b/src/main/java/neqsim/process/equipment/heatexchanger/MultiStreamHeatExchangerInterface.java
new file mode 100644
index 0000000000..4c61fbf5e8
--- /dev/null
+++ b/src/main/java/neqsim/process/equipment/heatexchanger/MultiStreamHeatExchangerInterface.java
@@ -0,0 +1,296 @@
+/*
+ * MultiStreamHeatExchangerInterface.java
+ *
+ * Created on [Date]
+ */
+
+package neqsim.process.equipment.heatexchanger;
+
+import java.util.UUID;
+import neqsim.process.equipment.ProcessEquipmentInterface;
+import neqsim.process.equipment.stream.StreamInterface;
+
+/**
+ *
+ * MultiStreamHeatExchangerInterface interface.
+ *
+ *
+ * Defines the contract for a multi-stream heat exchanger, enabling the simulation and management of
+ * multiple input and output streams. This interface extends the {@link ProcessEquipmentInterface}
+ * to integrate with the broader NeqSim process simulation framework.
+ *
+ *
+ * Implementations of this interface should handle the addition and management of multiple streams,
+ * perform energy and mass balance calculations, and provide methods to analyze the performance and
+ * condition of the heat exchanger.
+ *
+ *
+ * @author
+ * @version 1.0
+ */
+public interface MultiStreamHeatExchangerInterface extends ProcessEquipmentInterface {
+
+ // ================================
+ // Stream Management Methods
+ // ================================
+
+ /**
+ * Adds an inlet stream to the heat exchanger.
+ *
+ * @param inStream Input stream to be added
+ */
+ void addInStream(StreamInterface inStream);
+
+ /**
+ * Sets the feed stream at a specific index.
+ *
+ * @param index Index of the stream to set
+ * @param inStream Input stream to set at the specified index
+ * @throws IndexOutOfBoundsException if the index is out of range
+ */
+ void setFeedStream(int index, StreamInterface inStream);
+
+ /**
+ * Retrieves the output stream at the specified index.
+ *
+ * @param index Index of the output stream
+ * @return The output {@link StreamInterface} at the given index
+ * @throws IndexOutOfBoundsException if the index is out of range
+ */
+ StreamInterface getOutStream(int index);
+
+ /**
+ * Retrieves the input stream at the specified index.
+ *
+ * @param index Index of the input stream
+ * @return The input {@link StreamInterface} at the given index
+ * @throws IndexOutOfBoundsException if the index is out of range
+ */
+ StreamInterface getInStream(int index);
+
+ // ================================
+ // Temperature Control Methods
+ // ================================
+
+ /**
+ * Sets the outlet temperature for the heat exchanger.
+ *
+ * @param temperature Desired outlet temperature
+ */
+ void setOutTemperature(double temperature);
+
+ /**
+ * Gets the outlet temperature of a specific output stream.
+ *
+ * @param index Index of the output stream
+ * @return Outlet temperature in Kelvin
+ */
+ double getOutTemperature(int index);
+
+ /**
+ * Gets the inlet temperature of a specific input stream.
+ *
+ * @param index Index of the input stream
+ * @return Inlet temperature in Kelvin
+ */
+ double getInTemperature(int index);
+
+ /**
+ * Sets the temperature difference (ΔT) for the heat exchanger calculations.
+ *
+ * @param dT Temperature difference to set
+ */
+ void setdT(double dT);
+
+ /**
+ * Enables or disables the use of a fixed temperature difference (ΔT) in calculations.
+ *
+ * @param useDeltaT True to use ΔT, false otherwise
+ */
+ void setUseDeltaT(boolean useDeltaT);
+
+ /**
+ * Sets the fixed temperature difference (ΔT) for calculations and enables its usage.
+ *
+ * @param deltaT Fixed temperature difference to set
+ */
+ void setDeltaT(double deltaT);
+
+ /**
+ * Retrieves the fixed temperature difference (ΔT) used in calculations.
+ *
+ * @return Temperature difference ΔT
+ */
+ double getDeltaT();
+
+ // ================================
+ // Thermodynamic and Energy Methods
+ // ================================
+
+ /**
+ * Calculates and retrieves the thermal effectiveness of the heat exchanger.
+ *
+ * @return Thermal effectiveness
+ */
+ double getThermalEffectiveness();
+
+ /**
+ * Sets the thermal effectiveness of the heat exchanger.
+ *
+ * @param thermalEffectiveness Thermal effectiveness to set
+ */
+ void setThermalEffectiveness(double thermalEffectiveness);
+
+ /**
+ * Calculates the thermal effectiveness based on the Number of Transfer Units (NTU) and the
+ * capacity ratio (Cr).
+ *
+ * @param NTU Number of Transfer Units
+ * @param Cr Capacity ratio (Cmin/Cmax)
+ * @return Calculated thermal effectiveness
+ */
+ double calcThermalEffectiveness(double NTU, double Cr);
+
+ /**
+ * Retrieves the overall heat transfer coefficient times area (UA value).
+ *
+ * @return UA value
+ */
+ double getUAvalue();
+
+ /**
+ * Sets the overall heat transfer coefficient times area (UA value).
+ *
+ * @param UAvalue UA value to set
+ */
+ void setUAvalue(double UAvalue);
+
+ /**
+ * Retrieves the hot and cold duty balance of the heat exchanger.
+ *
+ * @return Hot and cold duty balance
+ */
+ double getHotColdDutyBalance();
+
+ /**
+ * Sets the hot and cold duty balance of the heat exchanger.
+ *
+ * @param hotColdDutyBalance Hot and cold duty balance to set
+ */
+ void setHotColdDutyBalance(double hotColdDutyBalance);
+
+ // ================================
+ // Duty and Balance Methods
+ // ================================
+
+ /**
+ * Retrieves the duty (heat transfer) of the heat exchanger.
+ *
+ * @return Duty in appropriate units
+ */
+ double getDuty();
+
+ /**
+ * Retrieves the mass balance of the heat exchanger.
+ *
+ * @param unit Unit of mass flow rate (e.g., "kg/sec")
+ * @return Mass balance value
+ */
+ double getMassBalance(String unit);
+
+ /**
+ * Retrieves the entropy production of the heat exchanger.
+ *
+ * @param unit Unit of entropy (e.g., "J/(kg*K)")
+ * @return Entropy production value
+ */
+ double getEntropyProduction(String unit);
+
+ // ================================
+ // Flow Arrangement Methods
+ // ================================
+
+ /**
+ * Retrieves the flow arrangement of the heat exchanger.
+ *
+ * @return Flow arrangement as a String (e.g., "counterflow", "parallelflow")
+ */
+ String getFlowArrangement();
+
+ /**
+ * Sets the flow arrangement of the heat exchanger.
+ *
+ * @param flowArrangement Name of the flow arrangement
+ */
+ void setFlowArrangement(String flowArrangement);
+
+ // ================================
+ // Condition Analysis Methods
+ // ================================
+
+ /**
+ * Runs a condition analysis by comparing the current heat exchanger with a reference exchanger.
+ *
+ * @param refExchanger Reference {@link ProcessEquipmentInterface} heat exchanger for comparison
+ */
+ void runConditionAnalysis(ProcessEquipmentInterface refExchanger);
+
+ /**
+ * Runs a condition analysis using the current heat exchanger as the reference.
+ */
+ void runConditionAnalysis();
+
+ // ================================
+ // Guess Temperature Methods
+ // ================================
+
+ /**
+ * Retrieves the guessed outlet temperature used during initialization.
+ *
+ * @return Guessed outlet temperature
+ */
+ double getGuessOutTemperature();
+
+ /**
+ * Sets the guessed outlet temperature in Kelvin.
+ *
+ * @param guessOutTemperature Guessed outlet temperature
+ */
+ void setGuessOutTemperature(double guessOutTemperature);
+
+ /**
+ * Sets the guessed outlet temperature with a specified unit.
+ *
+ * @param guessOutTemperature Guessed outlet temperature
+ * @param unit Unit of the temperature (e.g., "K", "C")
+ */
+ void setGuessOutTemperature(double guessOutTemperature, String unit);
+
+ // ================================
+ // JSON Serialization Methods
+ // ================================
+
+ /**
+ * Serializes the heat exchanger's state to a JSON string.
+ *
+ * @return JSON representation of the heat exchanger
+ */
+ String toJson();
+
+ // ================================
+ // Additional Methods
+ // ================================
+
+ /**
+ * Runs the heat exchanger simulation with a unique identifier.
+ *
+ * @param id Unique identifier for the simulation run
+ */
+ void run(UUID id);
+
+ /**
+ * Displays the results of the heat exchanger simulation. Typically outputs temperatures,
+ * pressures, flow rates, etc.
+ */
+ void displayResult();
+}
diff --git a/src/test/java/neqsim/process/equipment/heatexchanger/MultiStreamHeatExchangerTest.java b/src/test/java/neqsim/process/equipment/heatexchanger/MultiStreamHeatExchangerTest.java
new file mode 100644
index 0000000000..6648eaddb8
--- /dev/null
+++ b/src/test/java/neqsim/process/equipment/heatexchanger/MultiStreamHeatExchangerTest.java
@@ -0,0 +1,69 @@
+package neqsim.process.equipment.heatexchanger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import neqsim.process.equipment.stream.Stream;
+import neqsim.thermodynamicoperations.ThermodynamicOperations;
+
+public class MultiStreamHeatExchangerTest {
+ static Logger logger = LogManager.getLogger(MultiStreamHeatExchangerTest.class);
+
+ static neqsim.thermo.system.SystemInterface testSystem;
+ Stream gasStream;
+
+ @BeforeEach
+ void setUp() {
+ testSystem = new neqsim.thermo.system.SystemSrkEos((273.15 + 60.0), 20.00);
+ testSystem.addComponent("methane", 120.00);
+ testSystem.addComponent("ethane", 120.0);
+ testSystem.addComponent("n-heptane", 3.0);
+ testSystem.createDatabase(true);
+ testSystem.setMixingRule(2);
+ ThermodynamicOperations testOps = new ThermodynamicOperations(testSystem);
+ testOps.TPflash();
+ }
+
+
+ @Test
+ void testRun1() {
+ Stream stream_Hot = new Stream("Stream1", testSystem);
+ stream_Hot.setTemperature(100.0, "C");
+ stream_Hot.setFlowRate(1000.0, "kg/hr");
+
+ Stream stream_Cold = new Stream("Stream2", testSystem.clone());
+ stream_Cold.setTemperature(20.0, "C");
+ stream_Cold.setFlowRate(310.0, "kg/hr");
+
+ Stream stream_Cold2 = new Stream("Stream3", testSystem.clone());
+ stream_Cold2.setTemperature(0.0, "C");
+ stream_Cold2.setFlowRate(50.0, "kg/hr");
+
+ MultiStreamHeatExchanger heatEx = new MultiStreamHeatExchanger("heatEx");
+ heatEx.addInStream(stream_Hot);
+ heatEx.addInStream(stream_Cold);
+ heatEx.addInStream(stream_Cold2);
+ heatEx.setTemperatureApproach(5.0);
+
+
+ neqsim.process.processmodel.ProcessSystem operations =
+ new neqsim.process.processmodel.ProcessSystem();
+ operations.add(stream_Hot);
+ operations.add(stream_Cold);
+ operations.add(stream_Cold2);
+ operations.add(heatEx);
+
+ operations.run();
+
+ assertEquals(95, heatEx.getOutStream(1).getTemperature("C"), 1e-3);
+ assertEquals(95, heatEx.getOutStream(2).getTemperature("C"), 1e-3);
+ assertEquals(70.5921794735, heatEx.getOutStream(0).getTemperature("C"), 1e-3);
+
+ }
+
+
+
+}
+