From 83221794da81b2144a192defdfd74ba6be9ef7da Mon Sep 17 00:00:00 2001 From: Even Solbraa <41290109+EvenSol@users.noreply.github.com> Date: Sun, 17 Nov 2024 18:19:16 +0000 Subject: [PATCH 1/2] add extrapolate curve --- .../equipment/compressor/Compressor.java | 6 +- .../CompressorChartAlternativeMapLookup.java | 131 ++++++++++-------- ...rChartAlternativeMapLookupExtrapolate.java | 84 +++++++++++ .../compressor/CompressorInterface.java | 15 ++ .../compressor/CompressorChartTest.java | 5 +- 5 files changed, 178 insertions(+), 63 deletions(-) create mode 100644 src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookupExtrapolate.java 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..72b983f1d 100644 --- a/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookup.java +++ b/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookup.java @@ -13,8 +13,10 @@ import neqsim.thermo.system.SystemSrkEos; /** - * 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 + * 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). * * @author asmund @@ -54,7 +56,8 @@ public class CompressorChartAlternativeMapLookup * Constructor for CompressorChartAlternativeMapLookup. *

*/ - public CompressorChartAlternativeMapLookup() {} + public CompressorChartAlternativeMapLookup() { + } /** {@inheritDoc} */ @Override @@ -128,8 +131,7 @@ public double getPolytropicHead(double flow, double speed) { for (int i = 0; i < closestRefSpeeds.size(); i++) { s = closestRefSpeeds.get(i); // speedRatio = speed * gearRatio / s; - PolynomialSplineFunction psf = - asi.interpolate(getCurveAtRefSpeed(s).flow, getCurveAtRefSpeed(s).head); + PolynomialSplineFunction psf = asi.interpolate(getCurveAtRefSpeed(s).flow, getCurveAtRefSpeed(s).head); tempHeads.add(psf.value(flow)); } @@ -151,8 +153,8 @@ public double getPolytropicEfficiency(double flow, double speed) { for (int i = 0; i < closestRefSpeeds.size(); i++) { s = closestRefSpeeds.get(i); - PolynomialSplineFunction psf = - asi.interpolate(getCurveAtRefSpeed(s).flow, getCurveAtRefSpeed(s).polytropicEfficiency); + PolynomialSplineFunction psf = asi.interpolate(getCurveAtRefSpeed(s).flow, + getCurveAtRefSpeed(s).polytropicEfficiency); tempEffs.add(psf.value(flow)); } @@ -222,7 +224,7 @@ public void setGearRatio(double GR) { * polytropicEfficiency. *

* - * @param flow a double + * @param flow a double * @param speed a double * @return a double */ @@ -273,7 +275,7 @@ public boolean checkSurge1(double flow, double head) { * checkSurge2. *

* - * @param flow a double + * @param flow a double * @param speed a double * @return a boolean */ @@ -286,7 +288,7 @@ public boolean checkSurge2(double flow, double speed) { * checkStoneWall. *

* - * @param flow a double + * @param flow a double * @param speed a double * @return a boolean */ @@ -366,53 +368,58 @@ public static void main(String[] args) { // comp1.getAntiSurge().setActive(true); comp1.setSpeed(11918); - double[] chartConditions = new double[] {0.3, 1.0, 1.0, 1.0}; + double[] chartConditions = new double[] { 0.3, 1.0, 1.0, 1.0 }; /* - * double[] speed = new double[] { 1000.0, 2000.0, 3000.0, 4000.0 }; double[][] flow = new - * double[][] { { 453.2, 600.0, 750.0, 800.0 }, { 453.2, 600.0, 750.0, 800.0 }, { 453.2, 600.0, - * 750.0, 800.0 }, { 453.2, 600.0, 750.0, 800.0 } }; double[][] head = new double[][] { { - * 10000.0, 9000.0, 8000.0, 7500.0 }, { 10000.0, 9000.0, 8000.0, 7500.0 }, { 10000.0, 9000.0, - * 8000.0, 7500.0 }, { 10000.0, 9000.0, 8000.0, 7500.0 } }; double[][] polyEff = new double[][] - * { { 90.0, 91.0, 89.0, 88.0 }, { 90.0, 91.0, 89.0, 88.0 }, { 90.0, 91.0, 89.0, 88.1 }, { 90.0, + * double[] speed = new double[] { 1000.0, 2000.0, 3000.0, 4000.0 }; double[][] + * flow = new + * double[][] { { 453.2, 600.0, 750.0, 800.0 }, { 453.2, 600.0, 750.0, 800.0 }, + * { 453.2, 600.0, + * 750.0, 800.0 }, { 453.2, 600.0, 750.0, 800.0 } }; double[][] head = new + * double[][] { { + * 10000.0, 9000.0, 8000.0, 7500.0 }, { 10000.0, 9000.0, 8000.0, 7500.0 }, { + * 10000.0, 9000.0, + * 8000.0, 7500.0 }, { 10000.0, 9000.0, 8000.0, 7500.0 } }; double[][] polyEff = + * new double[][] + * { { 90.0, 91.0, 89.0, 88.0 }, { 90.0, 91.0, 89.0, 88.0 }, { 90.0, 91.0, 89.0, + * 88.1 }, { 90.0, * 91.0, 89.0, 88.1 } }; */ - double[] speed = new double[] {12913, 12298, 11683, 11098, 10453, 9224, 8609, 8200}; + double[] speed = new double[] { 12913, 12298, 11683, 11098, 10453, 9224, 8609, 8200 }; double[][] flow = new double[][] { - {2789.1285, 3174.0375, 3689.2288, 4179.4503, 4570.2768, 4954.7728, 5246.0329, 5661.0331}, - {2571.1753, 2943.7254, 3440.2675, 3837.4448, 4253.0898, 4668.6643, 4997.1926, 5387.4952}, - {2415.3793, 2763.0706, 3141.7095, 3594.7436, 4047.6467, 4494.1889, 4853.7353, 5138.7858}, - {2247.2043, 2799.7342, 3178.3428, 3656.1551, 4102.778, 4394.1591, 4648.3224, 4840.4998}, - {2072.8397, 2463.9483, 2836.4078, 3202.5266, 3599.6333, 3978.0203, 4257.0022, 4517.345}, - {1835.9552, 2208.455, 2618.1322, 2940.8034, 3244.7852, 3530.1279, 3753.3738, 3895.9746}, - {1711.3386, 1965.8848, 2356.9431, 2685.9247, 3008.5154, 3337.2855, 3591.5092}, - {1636.5807, 2002.8708, 2338.0319, 2642.1245, 2896.4894, 3113.6264, 3274.8764, 3411.2977}}; - double[][] head = - new double[][] {{80.0375, 78.8934, 76.2142, 71.8678, 67.0062, 60.6061, 53.0499, 39.728}, - {72.2122, 71.8369, 68.9009, 65.8341, 60.7167, 54.702, 47.2749, 35.7471}, - {65.1576, 64.5253, 62.6118, 59.1619, 54.0455, 47.0059, 39.195, 31.6387}, - {58.6154, 56.9627, 54.6647, 50.4462, 44.4322, 38.4144, 32.9084, 28.8109}, - {52.3295, 51.0573, 49.5283, 46.3326, 42.3685, 37.2502, 31.4884, 25.598}, - {40.6578, 39.6416, 37.6008, 34.6603, 30.9503, 27.1116, 23.2713, 20.4546}, - {35.2705, 34.6359, 32.7228, 31.0645, 27.0985, 22.7482, 18.0113}, - {32.192, 31.1756, 29.1329, 26.833, 23.8909, 21.3324, 18.7726, 16.3403}}; + { 2789.1285, 3174.0375, 3689.2288, 4179.4503, 4570.2768, 4954.7728, 5246.0329, 5661.0331 }, + { 2571.1753, 2943.7254, 3440.2675, 3837.4448, 4253.0898, 4668.6643, 4997.1926, 5387.4952 }, + { 2415.3793, 2763.0706, 3141.7095, 3594.7436, 4047.6467, 4494.1889, 4853.7353, 5138.7858 }, + { 2247.2043, 2799.7342, 3178.3428, 3656.1551, 4102.778, 4394.1591, 4648.3224, 4840.4998 }, + { 2072.8397, 2463.9483, 2836.4078, 3202.5266, 3599.6333, 3978.0203, 4257.0022, 4517.345 }, + { 1835.9552, 2208.455, 2618.1322, 2940.8034, 3244.7852, 3530.1279, 3753.3738, 3895.9746 }, + { 1711.3386, 1965.8848, 2356.9431, 2685.9247, 3008.5154, 3337.2855, 3591.5092 }, + { 1636.5807, 2002.8708, 2338.0319, 2642.1245, 2896.4894, 3113.6264, 3274.8764, 3411.2977 } }; + double[][] head = new double[][] { { 80.0375, 78.8934, 76.2142, 71.8678, 67.0062, 60.6061, 53.0499, 39.728 }, + { 72.2122, 71.8369, 68.9009, 65.8341, 60.7167, 54.702, 47.2749, 35.7471 }, + { 65.1576, 64.5253, 62.6118, 59.1619, 54.0455, 47.0059, 39.195, 31.6387 }, + { 58.6154, 56.9627, 54.6647, 50.4462, 44.4322, 38.4144, 32.9084, 28.8109 }, + { 52.3295, 51.0573, 49.5283, 46.3326, 42.3685, 37.2502, 31.4884, 25.598 }, + { 40.6578, 39.6416, 37.6008, 34.6603, 30.9503, 27.1116, 23.2713, 20.4546 }, + { 35.2705, 34.6359, 32.7228, 31.0645, 27.0985, 22.7482, 18.0113 }, + { 32.192, 31.1756, 29.1329, 26.833, 23.8909, 21.3324, 18.7726, 16.3403 } }; double[][] polyEff = new double[][] { - {77.2452238409573, 79.4154186459363, 80.737960012489, 80.5229826589649, 79.2210931638144, - 75.4719133864634, 69.6034181197298, 58.7322388482707}, - {77.0107837113504, 79.3069974136389, 80.8941189021135, 80.7190194665918, 79.5313242980328, - 75.5912622896367, 69.6846136362097, 60.0043057990909}, - {77.0043065299874, 79.1690958847856, 80.8038169975675, 80.6543975614197, 78.8532389102705, - 73.6664774270613, 66.2735600426727, 57.671664571658}, - {77.0716623789093, 80.4629750233093, 81.1390811169072, 79.6374242667478, 75.380928428817, - 69.5332969549779, 63.7997587622339, 58.8120614497758}, - {76.9705872525642, 79.8335492585324, 80.9468133671171, 80.5806471927835, 78.0462158225426, - 73.0403707523258, 66.5572286338589, 59.8624822515064}, - {77.5063036680357, 80.2056198362559, 81.0339108025933, 79.6085962687939, 76.3814534404405, - 70.8027503005902, 64.6437367160571, 60.5299349982342}, - {77.8175271586685, 80.065165942218, 81.0631362122632, 79.8955051771299, 76.1983240929369, - 69.289982774309, 60.8567149372229}, - {78.0924334304045, 80.9353551568667, 80.7904437766234, 78.8639325223295, 75.2170936751143, - 70.3105081673411, 65.5507568533569, 61.0391468300337}}; + { 77.2452238409573, 79.4154186459363, 80.737960012489, 80.5229826589649, 79.2210931638144, + 75.4719133864634, 69.6034181197298, 58.7322388482707 }, + { 77.0107837113504, 79.3069974136389, 80.8941189021135, 80.7190194665918, 79.5313242980328, + 75.5912622896367, 69.6846136362097, 60.0043057990909 }, + { 77.0043065299874, 79.1690958847856, 80.8038169975675, 80.6543975614197, 78.8532389102705, + 73.6664774270613, 66.2735600426727, 57.671664571658 }, + { 77.0716623789093, 80.4629750233093, 81.1390811169072, 79.6374242667478, 75.380928428817, + 69.5332969549779, 63.7997587622339, 58.8120614497758 }, + { 76.9705872525642, 79.8335492585324, 80.9468133671171, 80.5806471927835, 78.0462158225426, + 73.0403707523258, 66.5572286338589, 59.8624822515064 }, + { 77.5063036680357, 80.2056198362559, 81.0339108025933, 79.6085962687939, 76.3814534404405, + 70.8027503005902, 64.6437367160571, 60.5299349982342 }, + { 77.8175271586685, 80.065165942218, 81.0631362122632, 79.8955051771299, 76.1983240929369, + 69.289982774309, 60.8567149372229 }, + { 78.0924334304045, 80.9353551568667, 80.7904437766234, 78.8639325223295, 75.2170936751143, + 70.3105081673411, 65.5507568533569, 61.0391468300337 } }; // double[] chartConditions = new double[] { 0.3, 1.0, 1.0, 1.0 }; // double[] speed = new double[] { 13402.0 }; @@ -423,17 +430,20 @@ public static void main(String[] args) { comp1.getCompressorChart().setCurves(chartConditions, speed, flow, head, polyEff); // comp1.getCompressorChart().setHeadUnit("kJ/kg"); /* - * double[] surgeflow = new double[] { 453.2, 550.0, 700.0, 800.0 }; double[] surgehead = new + * double[] surgeflow = new double[] { 453.2, 550.0, 700.0, 800.0 }; double[] + * surgehead = new * double[] { 6000.0, 7000.0, 8000.0, 10000.0 }; - * comp1.getCompressorChart().getSurgeCurve().setCurve(chartConditions, surgeflow, surgehead); + * comp1.getCompressorChart().getSurgeCurve().setCurve(chartConditions, + * surgeflow, surgehead); * - * double[] stoneWallflow = new double[] { 923.2, 950.0, 980.0, 1000.0 }; double[] stoneWallHead + * double[] stoneWallflow = new double[] { 923.2, 950.0, 980.0, 1000.0 }; + * double[] stoneWallHead * = new double[] { 6000.0, 7000.0, 8000.0, 10000.0 }; - * comp1.getCompressorChart().getStoneWallCurve().setCurve(chartConditions, stoneWallflow, + * comp1.getCompressorChart().getStoneWallCurve().setCurve(chartConditions, + * stoneWallflow, * stoneWallHead); */ - neqsim.process.processmodel.ProcessSystem operations = - new neqsim.process.processmodel.ProcessSystem(); + neqsim.process.processmodel.ProcessSystem operations = new neqsim.process.processmodel.ProcessSystem(); operations.add(stream_1); operations.add(comp1); operations.run(); @@ -506,8 +516,8 @@ public static int bisect_left(Double[] A, double x) { * bisect_left. *

* - * @param A an array of {@link java.lang.Double} objects - * @param x a double + * @param A an array of {@link java.lang.Double} objects + * @param x a double * @param lo a int * @param hi a int * @return a int @@ -538,7 +548,8 @@ public static int bisect_left(Double[] A, double x, int lo, int hi) { /** {@inheritDoc} */ @Override - public void plot() {} + public void plot() { + } /** {@inheritDoc} */ @Override 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..02f422d65 --- /dev/null +++ b/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookupExtrapolate.java @@ -0,0 +1,84 @@ +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; + +/** + * 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). + * + * @author asmund + * @version $Id: $Id + */ +public class CompressorChartAlternativeMapLookupExtrapolate + extends CompressorChartAlternativeMapLookup { + private static final long serialVersionUID = 1000; + static Logger logger = LogManager.getLogger(CompressorChartAlternativeMapLookupExtrapolate.class); + + + /** + *

+ * Constructor for CompressorChartAlternativeMapLookupExtrapolate. + *

+ */ + public CompressorChartAlternativeMapLookupExtrapolate() {} + + @Override + public double getPolytropicHead(double flow, double speed) { + ArrayList closestRefSpeeds = getClosestRefSpeeds(speed); + SplineInterpolator interpolator = new SplineInterpolator(); + ArrayList interpolatedHeads = 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); + } + + return interpolatedHeads.stream().mapToDouble(Double::doubleValue).average().orElse(0.0); + } + + @Override + public double getPolytropicEfficiency(double flow, double speed) { + ArrayList closestRefSpeeds = getClosestRefSpeeds(speed); + SplineInterpolator interpolator = new SplineInterpolator(); + ArrayList interpolatedEfficiencies = new ArrayList<>(); + + for (double refSpeed : closestRefSpeeds) { + CompressorCurve curve = getCurveAtRefSpeed(refSpeed); + PolynomialSplineFunction spline = + interpolator.interpolate(curve.flow, curve.polytropicEfficiency); + + double efficiencyValue = + extrapolateOrInterpolate(flow, curve.flow, curve.polytropicEfficiency, spline); + interpolatedEfficiencies.add(efficiencyValue); + } + + return interpolatedEfficiencies.stream().mapToDouble(Double::doubleValue).average().orElse(0.0); + } + + private double extrapolateOrInterpolate(double flow, double[] flowData, double[] valueData, + PolynomialSplineFunction spline) { + double[] knots = spline.getKnots(); + if (flow < knots[0]) { + // Linear extrapolation below range using the first two points + double slope = (valueData[1] - valueData[0]) / (flowData[1] - flowData[0]); + return valueData[0] + slope * (flow - flowData[0]); + } else if (flow > knots[knots.length - 1]) { + // Linear extrapolation above range using the last two points + int last = flowData.length - 1; + double slope = + (valueData[last] - valueData[last - 1]) / (flowData[last] - flowData[last - 1]); + return valueData[last] + slope * (flow - flowData[last]); + } + // Interpolate within the range + return spline.value(flow); + } + +} 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..e07867f55 100644 --- a/src/test/java/neqsim/process/equipment/compressor/CompressorChartTest.java +++ b/src/test/java/neqsim/process/equipment/compressor/CompressorChartTest.java @@ -199,7 +199,7 @@ public void runCurveTest() { testFluid.setTemperature(29.96, "C"); testFluid.setPressure(75.73, "bara"); - testFluid.setTotalFlowRate(559401.418270102, "kg/hr"); + testFluid.setTotalFlowRate(359401.418270102, "kg/hr"); Stream stream_1 = new Stream("Stream1", testFluid); stream_1.run(); @@ -207,7 +207,7 @@ 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); @@ -262,6 +262,7 @@ public void runCurveTest() { System.out.println("surge flow rate margin " + comp1.getSurgeFlowRateMargin()); System.out.println("surge flow rate " + comp1.getSurgeFlowRate()); System.out.println("duty " + comp1.getPower("MW")); + System.out.println("surge " + comp1.isSurge()); } } From 0b5d7e638b85c7e0b0b32be1c37560125040be74 Mon Sep 17 00:00:00 2001 From: Even Solbraa <41290109+EvenSol@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:16:12 +0000 Subject: [PATCH 2/2] update test --- .../CompressorChartAlternativeMapLookup.java | 277 +++++++++++++----- ...rChartAlternativeMapLookupExtrapolate.java | 169 +++++++++-- .../compressor/CompressorChartTest.java | 27 +- 3 files changed, 371 insertions(+), 102 deletions(-) diff --git a/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookup.java b/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookup.java index 72b983f1d..8aa221c57 100644 --- a/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookup.java +++ b/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookup.java @@ -13,12 +13,115 @@ import neqsim.thermo.system.SystemSrkEos; /** - * 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 + * 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 */ @@ -56,8 +159,7 @@ public class CompressorChartAlternativeMapLookup * Constructor for CompressorChartAlternativeMapLookup. *

*/ - public CompressorChartAlternativeMapLookup() { - } + public CompressorChartAlternativeMapLookup() {} /** {@inheritDoc} */ @Override @@ -68,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) { @@ -88,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()]; @@ -119,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(); @@ -131,7 +263,8 @@ public double getPolytropicHead(double flow, double speed) { for (int i = 0; i < closestRefSpeeds.size(); i++) { s = closestRefSpeeds.get(i); // speedRatio = speed * gearRatio / s; - PolynomialSplineFunction psf = asi.interpolate(getCurveAtRefSpeed(s).flow, getCurveAtRefSpeed(s).head); + PolynomialSplineFunction psf = + asi.interpolate(getCurveAtRefSpeed(s).flow, getCurveAtRefSpeed(s).head); tempHeads.add(psf.value(flow)); } @@ -143,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(); @@ -153,8 +295,8 @@ public double getPolytropicEfficiency(double flow, double speed) { for (int i = 0; i < closestRefSpeeds.size(); i++) { s = closestRefSpeeds.get(i); - PolynomialSplineFunction psf = asi.interpolate(getCurveAtRefSpeed(s).flow, - getCurveAtRefSpeed(s).polytropicEfficiency); + PolynomialSplineFunction psf = + asi.interpolate(getCurveAtRefSpeed(s).flow, getCurveAtRefSpeed(s).polytropicEfficiency); tempEffs.add(psf.value(flow)); } @@ -224,7 +366,7 @@ public void setGearRatio(double GR) { * polytropicEfficiency. *

* - * @param flow a double + * @param flow a double * @param speed a double * @return a double */ @@ -275,7 +417,7 @@ public boolean checkSurge1(double flow, double head) { * checkSurge2. *

* - * @param flow a double + * @param flow a double * @param speed a double * @return a boolean */ @@ -288,7 +430,7 @@ public boolean checkSurge2(double flow, double speed) { * checkStoneWall. *

* - * @param flow a double + * @param flow a double * @param speed a double * @return a boolean */ @@ -368,58 +510,53 @@ public static void main(String[] args) { // comp1.getAntiSurge().setActive(true); comp1.setSpeed(11918); - double[] chartConditions = new double[] { 0.3, 1.0, 1.0, 1.0 }; + double[] chartConditions = new double[] {0.3, 1.0, 1.0, 1.0}; /* - * double[] speed = new double[] { 1000.0, 2000.0, 3000.0, 4000.0 }; double[][] - * flow = new - * double[][] { { 453.2, 600.0, 750.0, 800.0 }, { 453.2, 600.0, 750.0, 800.0 }, - * { 453.2, 600.0, - * 750.0, 800.0 }, { 453.2, 600.0, 750.0, 800.0 } }; double[][] head = new - * double[][] { { - * 10000.0, 9000.0, 8000.0, 7500.0 }, { 10000.0, 9000.0, 8000.0, 7500.0 }, { - * 10000.0, 9000.0, - * 8000.0, 7500.0 }, { 10000.0, 9000.0, 8000.0, 7500.0 } }; double[][] polyEff = - * new double[][] - * { { 90.0, 91.0, 89.0, 88.0 }, { 90.0, 91.0, 89.0, 88.0 }, { 90.0, 91.0, 89.0, - * 88.1 }, { 90.0, + * double[] speed = new double[] { 1000.0, 2000.0, 3000.0, 4000.0 }; double[][] flow = new + * double[][] { { 453.2, 600.0, 750.0, 800.0 }, { 453.2, 600.0, 750.0, 800.0 }, { 453.2, 600.0, + * 750.0, 800.0 }, { 453.2, 600.0, 750.0, 800.0 } }; double[][] head = new double[][] { { + * 10000.0, 9000.0, 8000.0, 7500.0 }, { 10000.0, 9000.0, 8000.0, 7500.0 }, { 10000.0, 9000.0, + * 8000.0, 7500.0 }, { 10000.0, 9000.0, 8000.0, 7500.0 } }; double[][] polyEff = new double[][] + * { { 90.0, 91.0, 89.0, 88.0 }, { 90.0, 91.0, 89.0, 88.0 }, { 90.0, 91.0, 89.0, 88.1 }, { 90.0, * 91.0, 89.0, 88.1 } }; */ - double[] speed = new double[] { 12913, 12298, 11683, 11098, 10453, 9224, 8609, 8200 }; + double[] speed = new double[] {12913, 12298, 11683, 11098, 10453, 9224, 8609, 8200}; double[][] flow = new double[][] { - { 2789.1285, 3174.0375, 3689.2288, 4179.4503, 4570.2768, 4954.7728, 5246.0329, 5661.0331 }, - { 2571.1753, 2943.7254, 3440.2675, 3837.4448, 4253.0898, 4668.6643, 4997.1926, 5387.4952 }, - { 2415.3793, 2763.0706, 3141.7095, 3594.7436, 4047.6467, 4494.1889, 4853.7353, 5138.7858 }, - { 2247.2043, 2799.7342, 3178.3428, 3656.1551, 4102.778, 4394.1591, 4648.3224, 4840.4998 }, - { 2072.8397, 2463.9483, 2836.4078, 3202.5266, 3599.6333, 3978.0203, 4257.0022, 4517.345 }, - { 1835.9552, 2208.455, 2618.1322, 2940.8034, 3244.7852, 3530.1279, 3753.3738, 3895.9746 }, - { 1711.3386, 1965.8848, 2356.9431, 2685.9247, 3008.5154, 3337.2855, 3591.5092 }, - { 1636.5807, 2002.8708, 2338.0319, 2642.1245, 2896.4894, 3113.6264, 3274.8764, 3411.2977 } }; - double[][] head = new double[][] { { 80.0375, 78.8934, 76.2142, 71.8678, 67.0062, 60.6061, 53.0499, 39.728 }, - { 72.2122, 71.8369, 68.9009, 65.8341, 60.7167, 54.702, 47.2749, 35.7471 }, - { 65.1576, 64.5253, 62.6118, 59.1619, 54.0455, 47.0059, 39.195, 31.6387 }, - { 58.6154, 56.9627, 54.6647, 50.4462, 44.4322, 38.4144, 32.9084, 28.8109 }, - { 52.3295, 51.0573, 49.5283, 46.3326, 42.3685, 37.2502, 31.4884, 25.598 }, - { 40.6578, 39.6416, 37.6008, 34.6603, 30.9503, 27.1116, 23.2713, 20.4546 }, - { 35.2705, 34.6359, 32.7228, 31.0645, 27.0985, 22.7482, 18.0113 }, - { 32.192, 31.1756, 29.1329, 26.833, 23.8909, 21.3324, 18.7726, 16.3403 } }; + {2789.1285, 3174.0375, 3689.2288, 4179.4503, 4570.2768, 4954.7728, 5246.0329, 5661.0331}, + {2571.1753, 2943.7254, 3440.2675, 3837.4448, 4253.0898, 4668.6643, 4997.1926, 5387.4952}, + {2415.3793, 2763.0706, 3141.7095, 3594.7436, 4047.6467, 4494.1889, 4853.7353, 5138.7858}, + {2247.2043, 2799.7342, 3178.3428, 3656.1551, 4102.778, 4394.1591, 4648.3224, 4840.4998}, + {2072.8397, 2463.9483, 2836.4078, 3202.5266, 3599.6333, 3978.0203, 4257.0022, 4517.345}, + {1835.9552, 2208.455, 2618.1322, 2940.8034, 3244.7852, 3530.1279, 3753.3738, 3895.9746}, + {1711.3386, 1965.8848, 2356.9431, 2685.9247, 3008.5154, 3337.2855, 3591.5092}, + {1636.5807, 2002.8708, 2338.0319, 2642.1245, 2896.4894, 3113.6264, 3274.8764, 3411.2977}}; + double[][] head = + new double[][] {{80.0375, 78.8934, 76.2142, 71.8678, 67.0062, 60.6061, 53.0499, 39.728}, + {72.2122, 71.8369, 68.9009, 65.8341, 60.7167, 54.702, 47.2749, 35.7471}, + {65.1576, 64.5253, 62.6118, 59.1619, 54.0455, 47.0059, 39.195, 31.6387}, + {58.6154, 56.9627, 54.6647, 50.4462, 44.4322, 38.4144, 32.9084, 28.8109}, + {52.3295, 51.0573, 49.5283, 46.3326, 42.3685, 37.2502, 31.4884, 25.598}, + {40.6578, 39.6416, 37.6008, 34.6603, 30.9503, 27.1116, 23.2713, 20.4546}, + {35.2705, 34.6359, 32.7228, 31.0645, 27.0985, 22.7482, 18.0113}, + {32.192, 31.1756, 29.1329, 26.833, 23.8909, 21.3324, 18.7726, 16.3403}}; double[][] polyEff = new double[][] { - { 77.2452238409573, 79.4154186459363, 80.737960012489, 80.5229826589649, 79.2210931638144, - 75.4719133864634, 69.6034181197298, 58.7322388482707 }, - { 77.0107837113504, 79.3069974136389, 80.8941189021135, 80.7190194665918, 79.5313242980328, - 75.5912622896367, 69.6846136362097, 60.0043057990909 }, - { 77.0043065299874, 79.1690958847856, 80.8038169975675, 80.6543975614197, 78.8532389102705, - 73.6664774270613, 66.2735600426727, 57.671664571658 }, - { 77.0716623789093, 80.4629750233093, 81.1390811169072, 79.6374242667478, 75.380928428817, - 69.5332969549779, 63.7997587622339, 58.8120614497758 }, - { 76.9705872525642, 79.8335492585324, 80.9468133671171, 80.5806471927835, 78.0462158225426, - 73.0403707523258, 66.5572286338589, 59.8624822515064 }, - { 77.5063036680357, 80.2056198362559, 81.0339108025933, 79.6085962687939, 76.3814534404405, - 70.8027503005902, 64.6437367160571, 60.5299349982342 }, - { 77.8175271586685, 80.065165942218, 81.0631362122632, 79.8955051771299, 76.1983240929369, - 69.289982774309, 60.8567149372229 }, - { 78.0924334304045, 80.9353551568667, 80.7904437766234, 78.8639325223295, 75.2170936751143, - 70.3105081673411, 65.5507568533569, 61.0391468300337 } }; + {77.2452238409573, 79.4154186459363, 80.737960012489, 80.5229826589649, 79.2210931638144, + 75.4719133864634, 69.6034181197298, 58.7322388482707}, + {77.0107837113504, 79.3069974136389, 80.8941189021135, 80.7190194665918, 79.5313242980328, + 75.5912622896367, 69.6846136362097, 60.0043057990909}, + {77.0043065299874, 79.1690958847856, 80.8038169975675, 80.6543975614197, 78.8532389102705, + 73.6664774270613, 66.2735600426727, 57.671664571658}, + {77.0716623789093, 80.4629750233093, 81.1390811169072, 79.6374242667478, 75.380928428817, + 69.5332969549779, 63.7997587622339, 58.8120614497758}, + {76.9705872525642, 79.8335492585324, 80.9468133671171, 80.5806471927835, 78.0462158225426, + 73.0403707523258, 66.5572286338589, 59.8624822515064}, + {77.5063036680357, 80.2056198362559, 81.0339108025933, 79.6085962687939, 76.3814534404405, + 70.8027503005902, 64.6437367160571, 60.5299349982342}, + {77.8175271586685, 80.065165942218, 81.0631362122632, 79.8955051771299, 76.1983240929369, + 69.289982774309, 60.8567149372229}, + {78.0924334304045, 80.9353551568667, 80.7904437766234, 78.8639325223295, 75.2170936751143, + 70.3105081673411, 65.5507568533569, 61.0391468300337}}; // double[] chartConditions = new double[] { 0.3, 1.0, 1.0, 1.0 }; // double[] speed = new double[] { 13402.0 }; @@ -430,20 +567,17 @@ public static void main(String[] args) { comp1.getCompressorChart().setCurves(chartConditions, speed, flow, head, polyEff); // comp1.getCompressorChart().setHeadUnit("kJ/kg"); /* - * double[] surgeflow = new double[] { 453.2, 550.0, 700.0, 800.0 }; double[] - * surgehead = new + * double[] surgeflow = new double[] { 453.2, 550.0, 700.0, 800.0 }; double[] surgehead = new * double[] { 6000.0, 7000.0, 8000.0, 10000.0 }; - * comp1.getCompressorChart().getSurgeCurve().setCurve(chartConditions, - * surgeflow, surgehead); + * comp1.getCompressorChart().getSurgeCurve().setCurve(chartConditions, surgeflow, surgehead); * - * double[] stoneWallflow = new double[] { 923.2, 950.0, 980.0, 1000.0 }; - * double[] stoneWallHead + * double[] stoneWallflow = new double[] { 923.2, 950.0, 980.0, 1000.0 }; double[] stoneWallHead * = new double[] { 6000.0, 7000.0, 8000.0, 10000.0 }; - * comp1.getCompressorChart().getStoneWallCurve().setCurve(chartConditions, - * stoneWallflow, + * comp1.getCompressorChart().getStoneWallCurve().setCurve(chartConditions, stoneWallflow, * stoneWallHead); */ - neqsim.process.processmodel.ProcessSystem operations = new neqsim.process.processmodel.ProcessSystem(); + neqsim.process.processmodel.ProcessSystem operations = + new neqsim.process.processmodel.ProcessSystem(); operations.add(stream_1); operations.add(comp1); operations.run(); @@ -516,8 +650,8 @@ public static int bisect_left(Double[] A, double x) { * bisect_left. *

* - * @param A an array of {@link java.lang.Double} objects - * @param x a double + * @param A an array of {@link java.lang.Double} objects + * @param x a double * @param lo a int * @param hi a int * @return a int @@ -548,8 +682,7 @@ public static int bisect_left(Double[] A, double x, int lo, int hi) { /** {@inheritDoc} */ @Override - public void plot() { - } + public void plot() {} /** {@inheritDoc} */ @Override diff --git a/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookupExtrapolate.java b/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookupExtrapolate.java index 02f422d65..01ab16f5d 100644 --- a/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookupExtrapolate.java +++ b/src/main/java/neqsim/process/equipment/compressor/CompressorChartAlternativeMapLookupExtrapolate.java @@ -6,14 +6,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * 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). - * - * @author asmund - * @version $Id: $Id - */ public class CompressorChartAlternativeMapLookupExtrapolate extends CompressorChartAlternativeMapLookup { private static final long serialVersionUID = 1000; @@ -21,17 +13,65 @@ public class CompressorChartAlternativeMapLookupExtrapolate /** - *

- * Constructor for CompressorChartAlternativeMapLookupExtrapolate. - *

+ * 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. */ - public CompressorChartAlternativeMapLookupExtrapolate() {} + @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); @@ -39,46 +79,139 @@ public double getPolytropicHead(double flow, double speed) { double headValue = extrapolateOrInterpolate(flow, curve.flow, curve.head, spline); interpolatedHeads.add(headValue); + speeds.add(refSpeed); + } + + if (interpolatedHeads.size() == 1) { + return interpolatedHeads.get(0); } - return interpolatedHeads.stream().mapToDouble(Double::doubleValue).average().orElse(0.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); } - return interpolatedEfficiencies.stream().mapToDouble(Double::doubleValue).average().orElse(0.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]) { - // Linear extrapolation below range using the first two points + 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]) { - // Linear extrapolation above range using the last two points + 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]); } - // Interpolate within the range + + 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/test/java/neqsim/process/equipment/compressor/CompressorChartTest.java b/src/test/java/neqsim/process/equipment/compressor/CompressorChartTest.java index e07867f55..0896e75de 100644 --- a/src/test/java/neqsim/process/equipment/compressor/CompressorChartTest.java +++ b/src/test/java/neqsim/process/equipment/compressor/CompressorChartTest.java @@ -199,7 +199,7 @@ public void runCurveTest() { testFluid.setTemperature(29.96, "C"); testFluid.setPressure(75.73, "bara"); - testFluid.setTotalFlowRate(359401.418270102, "kg/hr"); + testFluid.setTotalFlowRate(559401.418270102, "kg/hr"); Stream stream_1 = new Stream("Stream1", testFluid); stream_1.run(); @@ -210,6 +210,7 @@ public void runCurveTest() { 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,17 +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")); - System.out.println("surge " + comp1.isSurge()); + 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); } }