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: + *

+ * + * + *

+ * Methods: + *

+ * + * + *

+ * Exceptions: + *

+ * + * + * @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); } }