diff --git a/src/main/java/neqsim/process/equipment/compressor/Compressor.java b/src/main/java/neqsim/process/equipment/compressor/Compressor.java
index 8da288cc0..ac78022d0 100644
--- a/src/main/java/neqsim/process/equipment/compressor/Compressor.java
+++ b/src/main/java/neqsim/process/equipment/compressor/Compressor.java
@@ -1475,8 +1475,12 @@ public double getActualCompressionRatio() {
public void setCompressorChartType(String type) {
if (type.equals("simple")) {
compressorChart = new CompressorChart();
- } else {
+ } else if (type.equals("interpolate")) {
compressorChart = new CompressorChartAlternativeMapLookup();
+ } else if (type.equals("interpolate and extrapolate")) {
+ compressorChart = new CompressorChartAlternativeMapLookupExtrapolate();
+ } else {
+ compressorChart = new CompressorChart();
}
}
}
diff --git a/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookup.java b/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookup.java
index 7e4895eb5..8aa221c57 100644
--- a/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookup.java
+++ b/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookup.java
@@ -16,7 +16,112 @@
* This class is an implementation of the compressor chart class that uses Fan laws and "double"
* interpolation to navigate the compressor map (as opposed to the standard class using reduced
* variables according to Fan laws).
- *
+ *
+ *
+ * The class provides methods to add compressor curves, set reference conditions, and calculate
+ * polytropic head and efficiency based on flow and speed. It also includes methods to check surge
+ * and stone wall conditions.
+ *
+ *
+ *
+ * The main method demonstrates the usage of the class by creating a test fluid, setting up a
+ * compressor, and running a process system.
+ *
+ *
+ *
+ * The class implements the CompressorChartInterface and is Serializable.
+ *
+ *
+ *
+ * Fields:
+ *
+ *
+ * - serialVersionUID: A unique identifier for serialization.
+ * - logger: Logger instance for logging.
+ * - chartValues: List of compressor curves.
+ * - chartSpeeds: List of chart speeds.
+ * - surgeCurve: Surge curve instance.
+ * - stoneWallCurve: Stone wall curve instance.
+ * - isSurge: Flag indicating if the compressor is in surge condition.
+ * - isStoneWall: Flag indicating if the compressor is in stone wall condition.
+ * - refMW: Reference molecular weight.
+ * - headUnit: Unit of the head (default is "meter").
+ * - useCompressorChart: Flag indicating if the compressor chart is used.
+ * - refTemperature: Reference temperature.
+ * - refPressure: Reference pressure.
+ * - referenceSpeed: Reference speed (default is 1000.0).
+ * - refZ: Reference compressibility factor.
+ * - useRealKappa: Flag indicating if real kappa is used.
+ * - chartConditions: Array of chart conditions.
+ * - reducedHeadFitter: Weighted observed points for reduced head fitting.
+ * - reducedFlowFitter: Weighted observed points for reduced flow fitting.
+ * - fanLawCorrectionFitter: Weighted observed points for fan law correction fitting.
+ * - reducedPolytropicEfficiencyFitter: Weighted observed points for reduced polytropic efficiency
+ * fitting.
+ * - reducedHeadFitterFunc: Polynomial function for reduced head fitting.
+ * - reducedPolytropicEfficiencyFunc: Polynomial function for reduced polytropic efficiency
+ * fitting.
+ * - fanLawCorrectionFunc: Polynomial function for fan law correction fitting.
+ * - gearRatio: Gear ratio (default is 1.0).
+ *
+ *
+ *
+ * Methods:
+ *
+ *
+ * - addCurve: Adds a compressor curve.
+ * - setCurves: Sets multiple compressor curves.
+ * - getClosestRefSpeeds: Gets the closest reference speeds to a given speed.
+ * - getPolytropicHead: Calculates the polytropic head based on flow and speed.
+ * - getPolytropicEfficiency: Calculates the polytropic efficiency based on flow and speed.
+ * - addSurgeCurve: Adds a surge curve.
+ * - getCurveAtRefSpeed: Gets the compressor curve at a given reference speed.
+ * - getGearRatio: Gets the gear ratio.
+ * - setGearRatio: Sets the gear ratio.
+ * - polytropicEfficiency: Calculates the polytropic efficiency (returns a constant value of
+ * 100.0).
+ * - getSpeed: Calculates the speed based on flow and head.
+ * - checkSurge1: Checks if the compressor is in surge condition (method 1).
+ * - checkSurge2: Checks if the compressor is in surge condition (method 2).
+ * - checkStoneWall: Checks if the compressor is in stone wall condition.
+ * - setReferenceConditions: Sets the reference conditions.
+ * - getSurgeCurve: Gets the surge curve.
+ * - setSurgeCurve: Sets the surge curve.
+ * - getStoneWallCurve: Gets the stone wall curve.
+ * - setStoneWallCurve: Sets the stone wall curve.
+ * - main: Main method demonstrating the usage of the class.
+ * - isUseCompressorChart: Checks if the compressor chart is used.
+ * - setUseCompressorChart: Sets the flag indicating if the compressor chart is used.
+ * - getHeadUnit: Gets the unit of the head.
+ * - setHeadUnit: Sets the unit of the head.
+ * - useRealKappa: Checks if real kappa is used.
+ * - setUseRealKappa: Sets the flag indicating if real kappa is used.
+ * - bisect_left: Helper method for binary search (overloaded).
+ * - plot: Placeholder method for plotting (not implemented).
+ * - getFlow: Placeholder method for getting flow (not implemented).
+ *
+ *
+ *
+ * Exceptions:
+ *
+ *
+ * - RuntimeException: Thrown for invalid input or unsupported head unit value.
+ *
+ *
+ * @see neqsim.process.equipment.compressor.CompressorChartInterface
+ * @see java.io.Serializable
+ * @see org.apache.commons.math3.analysis.interpolation.SplineInterpolator
+ * @see org.apache.commons.math3.analysis.polynomials.PolynomialFunction
+ * @see org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction
+ * @see org.apache.commons.math3.fitting.WeightedObservedPoints
+ * @see org.apache.logging.log4j.LogManager
+ * @see org.apache.logging.log4j.Logger
+ * @see neqsim.process.equipment.stream.Stream
+ * @see neqsim.thermo.system.SystemInterface
+ * @see neqsim.thermo.system.SystemSrkEos
+ */
+
+/**
* @author asmund
* @version $Id: $Id
*/
@@ -65,6 +170,16 @@ public void addCurve(double speed, double[] flow, double[] head, double[] polytr
}
/** {@inheritDoc} */
+ /**
+ * Sets the compressor curves based on the provided chart conditions, speed, flow, head, and
+ * polytropic efficiency values.
+ *
+ * @param chartConditions an array of chart conditions (not used in this method)
+ * @param speed an array of speed values for the compressor
+ * @param flow a 2D array of flow values corresponding to each speed
+ * @param head a 2D array of head values corresponding to each speed
+ * @param polyEff a 2D array of polytropic efficiency values corresponding to each speed
+ */
@Override
public void setCurves(double[] chartConditions, double[] speed, double[][] flow, double[][] head,
double[][] polyEff) {
@@ -85,6 +200,16 @@ public void setCurves(double[] chartConditions, double[] speed, double[][] flow,
* @param speed a double
* @return a {@link java.util.ArrayList} object
*/
+ /**
+ * Returns a list of the closest reference speeds to the given speed. If the given speed matches a
+ * reference speed, only that speed is returned. If the given speed is between two reference
+ * speeds, both are returned. If the given speed is lower than the lowest reference speed, the
+ * lowest reference speed is returned. If the given speed is higher than the highest reference
+ * speed, the highest reference speed is returned.
+ *
+ * @param speed the speed to find the closest reference speeds for
+ * @return an ArrayList of the closest reference speeds
+ */
public ArrayList getClosestRefSpeeds(double speed) {
ArrayList closestRefSpeeds = new ArrayList();
Double[] speedArray = new Double[chartSpeeds.size()];
@@ -116,6 +241,16 @@ public ArrayList getClosestRefSpeeds(double speed) {
}
/** {@inheritDoc} */
+ /**
+ * Calculates the polytropic head for a given flow and speed.
+ *
+ * This method interpolates the polytropic head values from reference speeds closest to the given
+ * speed and averages them to estimate the polytropic head at the specified flow and speed.
+ *
+ * @param flow the flow rate for which the polytropic head is to be calculated
+ * @param speed the speed at which the polytropic head is to be calculated
+ * @return the calculated polytropic head
+ */
@Override
public double getPolytropicHead(double flow, double speed) {
ArrayList closestRefSpeeds = new ArrayList();
@@ -141,6 +276,15 @@ public double getPolytropicHead(double flow, double speed) {
}
/** {@inheritDoc} */
+ /**
+ * Calculates the polytropic efficiency of the compressor for a given flow and speed. The method
+ * interpolates the efficiency values from reference speed curves and averages them to estimate
+ * the efficiency at the specified conditions.
+ *
+ * @param flow the flow rate through the compressor
+ * @param speed the rotational speed of the compressor
+ * @return the polytropic efficiency at the specified flow and speed
+ */
@Override
public double getPolytropicEfficiency(double flow, double speed) {
ArrayList closestRefSpeeds = new ArrayList();
diff --git a/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookupExtrapolate.java b/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookupExtrapolate.java
new file mode 100644
index 000000000..01ab16f5d
--- /dev/null
+++ b/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookupExtrapolate.java
@@ -0,0 +1,217 @@
+package neqsim.process.equipment.compressor;
+
+import java.util.ArrayList;
+import org.apache.commons.math3.analysis.interpolation.SplineInterpolator;
+import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+public class CompressorChartAlternativeMapLookupExtrapolate
+ extends CompressorChartAlternativeMapLookup {
+ private static final long serialVersionUID = 1000;
+ static Logger logger = LogManager.getLogger(CompressorChartAlternativeMapLookupExtrapolate.class);
+
+
+ /**
+ * Retrieves the closest reference speeds to the given speed from the compressor chart values. The
+ * method returns a list containing one or two speeds: - If the given speed matches a reference
+ * speed, the list contains only that speed. - If the given speed is between two reference speeds,
+ * the list contains both speeds. - If the given speed is less than the lowest reference speed,
+ * the list contains the lowest reference speed. - If the given speed is greater than the highest
+ * reference speed, the list contains the highest reference speed.
+ *
+ * @param speed the speed to find the closest reference speeds for.
+ * @return a list of the closest reference speeds.
+ * @throws IllegalStateException if no reference speeds are available in the chart values.
+ */
+ @Override
+ public ArrayList getClosestRefSpeeds(double speed) {
+ ArrayList closestSpeeds = new ArrayList<>();
+ for (CompressorCurve curve : chartValues) {
+ closestSpeeds.add(curve.speed);
+ }
+
+ if (closestSpeeds.isEmpty()) {
+ throw new IllegalStateException(
+ "No reference speeds available. Ensure chartValues is populated.");
+ }
+
+ closestSpeeds.sort(Double::compareTo);
+
+ ArrayList result = new ArrayList<>();
+ for (int i = 0; i < closestSpeeds.size(); i++) {
+ if (speed == closestSpeeds.get(i)) {
+ result.add(speed);
+ return result;
+ }
+ if (speed < closestSpeeds.get(i)) {
+ if (i > 0) {
+ result.add(closestSpeeds.get(i - 1));
+ }
+ result.add(closestSpeeds.get(i));
+ return result;
+ }
+ }
+
+ // Speed is greater than the highest reference speed
+ result.add(closestSpeeds.get(closestSpeeds.size() - 1));
+ return result;
+ }
+
+ /**
+ * Calculates the polytropic head for a given flow and speed by interpolating or extrapolating
+ * between reference compressor curves.
+ *
+ * @param flow the flow rate for which the polytropic head is to be calculated
+ * @param speed the speed at which the compressor is operating
+ * @return the polytropic head corresponding to the given flow and speed
+ */
+ @Override
+ public double getPolytropicHead(double flow, double speed) {
+ ArrayList closestRefSpeeds = getClosestRefSpeeds(speed);
+ SplineInterpolator interpolator = new SplineInterpolator();
+ ArrayList interpolatedHeads = new ArrayList<>();
+ ArrayList speeds = new ArrayList<>();
+
+ for (double refSpeed : closestRefSpeeds) {
+ CompressorCurve curve = getCurveAtRefSpeed(refSpeed);
+ PolynomialSplineFunction spline = interpolator.interpolate(curve.flow, curve.head);
+
+ double headValue = extrapolateOrInterpolate(flow, curve.flow, curve.head, spline);
+ interpolatedHeads.add(headValue);
+ speeds.add(refSpeed);
+ }
+
+ if (interpolatedHeads.size() == 1) {
+ return interpolatedHeads.get(0);
+ }
+
+ double speed1 = speeds.get(0);
+ double speed2 = speeds.get(1);
+ double head1 = interpolatedHeads.get(0);
+ double head2 = interpolatedHeads.get(1);
+
+ return extrapolateOrInterpolateSpeed(speed, speed1, speed2, head1, head2);
+ }
+
+ /**
+ * Calculates the polytropic efficiency for a given flow and speed by interpolating or
+ * extrapolating between reference compressor curves.
+ *
+ * @param flow the flow rate for which the polytropic efficiency is to be calculated
+ * @param speed the speed at which the compressor is operating
+ * @return the polytropic efficiency at the given flow and speed
+ * @throws IllegalArgumentException if no valid reference speeds are found for the given speed or
+ * if the curve data for a reference speed is invalid
+ */
+ @Override
+ public double getPolytropicEfficiency(double flow, double speed) {
+ ArrayList closestRefSpeeds = getClosestRefSpeeds(speed);
+
+ if (closestRefSpeeds.isEmpty()) {
+ throw new IllegalArgumentException(
+ "No valid reference speeds found for the given speed: " + speed);
+ }
+
+ SplineInterpolator interpolator = new SplineInterpolator();
+ ArrayList interpolatedEfficiencies = new ArrayList<>();
+ ArrayList speeds = new ArrayList<>();
+
+ for (double refSpeed : closestRefSpeeds) {
+ CompressorCurve curve = getCurveAtRefSpeed(refSpeed);
+
+ if (curve.flow.length == 0 || curve.polytropicEfficiency.length == 0) {
+ throw new IllegalArgumentException("Invalid curve data for speed: " + refSpeed);
+ }
+
+ PolynomialSplineFunction spline =
+ interpolator.interpolate(curve.flow, curve.polytropicEfficiency);
+ double efficiencyValue =
+ extrapolateOrInterpolate(flow, curve.flow, curve.polytropicEfficiency, spline);
+ interpolatedEfficiencies.add(efficiencyValue);
+ speeds.add(refSpeed);
+ }
+
+ if (interpolatedEfficiencies.size() == 1) {
+ return interpolatedEfficiencies.get(0);
+ }
+
+ double speed1 = speeds.get(0);
+ double speed2 = speeds.get(1);
+ double eff1 = interpolatedEfficiencies.get(0);
+ double eff2 = interpolatedEfficiencies.get(1);
+
+ return extrapolateOrInterpolateSpeed(speed, speed1, speed2, eff1, eff2);
+ }
+
+ /**
+ * Extrapolates or interpolates a value based on the given flow using the provided flow data,
+ * value data, and polynomial spline function.
+ *
+ * @param flow the flow value for which the corresponding value needs to be determined
+ * @param flowData an array of flow data points
+ * @param valueData an array of value data points corresponding to the flow data points
+ * @param spline a polynomial spline function created from the flow and value data points
+ * @return the extrapolated or interpolated value corresponding to the given flow
+ */
+ private double extrapolateOrInterpolate(double flow, double[] flowData, double[] valueData,
+ PolynomialSplineFunction spline) {
+ double[] knots = spline.getKnots();
+
+ if (flow < knots[0]) {
+ logger.debug("Extrapolating below range: flow={}, knots[0]={}", flow, knots[0]);
+ double slope = (valueData[1] - valueData[0]) / (flowData[1] - flowData[0]);
+ return valueData[0] + slope * (flow - flowData[0]);
+ } else if (flow > knots[knots.length - 1]) {
+ logger.debug("Extrapolating above range: flow={}, knots[last]={}", flow,
+ knots[knots.length - 1]);
+ int last = flowData.length - 1;
+ double slope =
+ (valueData[last] - valueData[last - 1]) / (flowData[last] - flowData[last - 1]);
+ return valueData[last] + slope * (flow - flowData[last]);
+ }
+
+ logger.debug("Interpolating within range: flow={}", flow);
+ return spline.value(flow);
+ }
+
+ /**
+ * Extrapolates or interpolates a value based on the given speed and two reference speeds with
+ * their corresponding values.
+ *
+ * @param speed the speed at which to extrapolate or interpolate the value
+ * @param speed1 the first reference speed
+ * @param speed2 the second reference speed
+ * @param value1 the value corresponding to the first reference speed
+ * @param value2 the value corresponding to the second reference speed
+ * @return the extrapolated or interpolated value at the given speed
+ */
+ private double extrapolateOrInterpolateSpeed(double speed, double speed1, double speed2,
+ double value1, double value2) {
+ if (speed < speed1) {
+ // Extrapolate below the range
+ double slope = (value2 - value1) / (speed2 - speed1);
+ return value1 + slope * (speed - speed1);
+ } else if (speed > speed2) {
+ // Extrapolate above the range
+ double slope = (value2 - value1) / (speed2 - speed1);
+ return value2 + slope * (speed - speed2);
+ }
+ return linearInterpolate(speed, speed1, speed2, value1, value2); // Interpolate within the range
+ }
+
+ /**
+ * Performs linear interpolation to estimate the value of y at a given x, based on two known
+ * points (x1, y1) and (x2, y2).
+ *
+ * @param x the x-value at which to interpolate
+ * @param x1 the x-value of the first known point
+ * @param x2 the x-value of the second known point
+ * @param y1 the y-value of the first known point
+ * @param y2 the y-value of the second known point
+ * @return the interpolated y-value at the given x
+ */
+ private double linearInterpolate(double x, double x1, double x2, double y1, double y2) {
+ return y1 + (y2 - y1) * (x - x1) / (x2 - x1);
+ }
+}
diff --git a/src/main/java/neqsim/process/equipment/compressor/CompressorInterface.java b/src/main/java/neqsim/process/equipment/compressor/CompressorInterface.java
index 184862452..103bd51e2 100644
--- a/src/main/java/neqsim/process/equipment/compressor/CompressorInterface.java
+++ b/src/main/java/neqsim/process/equipment/compressor/CompressorInterface.java
@@ -142,4 +142,19 @@ public interface CompressorInterface extends ProcessEquipmentInterface, TwoPortI
*
*/
public void setCompressorChartType(String type);
+
+ /**
+ *
+ * isSurge.
+ *
+ *
+ * @return a boolean
+ */
+ default boolean isSurge() {
+ if (getDistanceToSurge() < 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
}
diff --git a/src/test/java/neqsim/process/equipment/compressor/CompressorChartTest.java b/src/test/java/neqsim/process/equipment/compressor/CompressorChartTest.java
index e0c0b8c9b..0896e75de 100644
--- a/src/test/java/neqsim/process/equipment/compressor/CompressorChartTest.java
+++ b/src/test/java/neqsim/process/equipment/compressor/CompressorChartTest.java
@@ -207,9 +207,10 @@ public void runCurveTest() {
stream_1.getFluid().prettyPrint();
Compressor comp1 = new Compressor("compressor 1", stream_1);
- //comp1.setCompressorChartType("interpolate");
+ comp1.setCompressorChartType("interpolate and extrapolate");
comp1.setUsePolytropicCalc(true);
comp1.setSpeed(8765);
+ comp1.setUseGERG2008(false);
double[] chartConditions = new double[] {0.3, 1.0, 1.0, 1.0};
@@ -252,16 +253,19 @@ public void runCurveTest() {
comp1.getAntiSurge().setSurgeControlFactor(1.0);
comp1.run();
- System.out.println("speed " + comp1.getSpeed());
- System.out.println("out pres " + comp1.getOutletPressure());
- System.out.println("out temp " + (comp1.getOutTemperature() - 273.15));
- System.out.println("feed flow " + (comp1.getInletStream().getFlowRate("m3/hr")));
- System.out.println("polytropic head " + comp1.getPolytropicFluidHead());
- System.out.println("polytropic efficiency " + comp1.getPolytropicEfficiency());
- System.out.println("dist to surge " + comp1.getDistanceToSurge());
- System.out.println("surge flow rate margin " + comp1.getSurgeFlowRateMargin());
- System.out.println("surge flow rate " + comp1.getSurgeFlowRate());
- System.out.println("duty " + comp1.getPower("MW"));
+ org.apache.logging.log4j.LogManager.getLogger(CompressorChartTest.class).debug("speed " + comp1.getSpeed());
+ org.apache.logging.log4j.LogManager.getLogger(CompressorChartTest.class).debug("out pres " + comp1.getOutletPressure());
+ org.apache.logging.log4j.LogManager.getLogger(CompressorChartTest.class).debug("out temp " + (comp1.getOutTemperature() - 273.15));
+ org.apache.logging.log4j.LogManager.getLogger(CompressorChartTest.class).debug("feed flow " + (comp1.getInletStream().getFlowRate("m3/hr")));
+ org.apache.logging.log4j.LogManager.getLogger(CompressorChartTest.class).debug("polytropic head " + comp1.getPolytropicFluidHead());
+ org.apache.logging.log4j.LogManager.getLogger(CompressorChartTest.class).debug("polytropic efficiency " + comp1.getPolytropicEfficiency());
+ org.apache.logging.log4j.LogManager.getLogger(CompressorChartTest.class).debug("dist to surge " + comp1.getDistanceToSurge());
+ org.apache.logging.log4j.LogManager.getLogger(CompressorChartTest.class).debug("surge flow rate margin " + comp1.getSurgeFlowRateMargin());
+ org.apache.logging.log4j.LogManager.getLogger(CompressorChartTest.class).debug("surge flow rate " + comp1.getSurgeFlowRate());
+ org.apache.logging.log4j.LogManager.getLogger(CompressorChartTest.class).debug("duty " + comp1.getPower("MW"));
+ org.apache.logging.log4j.LogManager.getLogger(CompressorChartTest.class).debug("surge " + comp1.isSurge());
+ Assertions.assertTrue(comp1.isSurge() == false);
+ Assertions.assertEquals(158.7732888, comp1.getOutletPressure(), 1e-3);
}
}