Skip to content

SaturationTemperature remains ~10x slower after #3271 for repeated UMR-PRU dew-point tuning #3795

Description

@AndreasHNyhus

Describe the bug

SaturationTemperature.calcSaturationTemperature() remains approximately 10x slower in NeqSim 3.20.0 than in 3.1.5 for repeated UMR-PRU dew-point tuning, despite the partial performance repair in #3271 / commit af6d5be4.

This is a follow-up to #3269. That issue correctly identified the full-range saturation scan introduced by #2219 / commit ec18449b4. The v3.19 fix stops after finding the uppermost crossing, but every call still starts at 1200 K and descends in 10 K increments. For natural-gas dew points near 290 K this still requires about 90 coarse TPflash calls before bisection.

The cost is amplified when SaturationTemperature is called repeatedly by an outer root finder. A representative 24-component UMR-PRU tuning workload calls it approximately 22 times per request.

A direct parent/commit A/B test attributes the original regression to ec18449b4:

Revision Median per saturation call TP flashes Saturation temperature
25f0d281 (parent) 0.287 s 24 17.913278198 C
ec18449b4 4.649 s 139 17.913280106 C

That is a 16.2x slowdown with a numerical difference of only about 1.9e-6 C for this case.

Application-level warm timings with identical current caller code and identical input are:

jneqsim version Warm request time Relative to 3.1.5
3.1.5 3.724 s 1.0x
3.18.0 56.136 s 15.1x
3.20.0 38.144 s 10.2x

Thus #3271 improved 3.18 performance, but did not restore the pre-3.12 behavior for near-boundary iterative workloads.

To Reproduce

Run the following with jneqsim==3.1.5 and jneqsim==3.20.0. Use the same Python caller and run at least twice in one process to separate JVM/JIT startup from steady-state time.

import time

from jneqsim import neqsim

composition = {
    "CO2": 0.00645,
    "nitrogen": 0.00966,
    "methane": 0.949,
    "ethane": 0.0258,
    "propane": 0.00352,
    "i-butane": 0.00152,
    "n-butane": 0.000881,
    "i-pentane": 0.00079,
    "n-pentane": 0.000395,
    "2-m-C5": 0.000401,
    "3-m-C5": 0.000122,
    "n-hexane": 0.000198,
    "n-heptane": 0.000133,
    "c-hexane": 0.000798,
    "benzene": 1.65e-5,
    "n-octane": 3.39e-5,
    "c-C7": 0.000487,
    "toluene": 3.62e-5,
    "n-nonane": 2.57e-5,
    "c-C8": 5.81e-5,
    "m-Xylene": 1.7e-5,
    "nC10": 1.16e-5,
    "nC11": 0.0,
    "nC12": 0.0,
}

fluid = neqsim.thermo.system.SystemUMRPRUMCEos(280.0, 52.1)
for name, amount in composition.items():
    fluid.addComponent(name, amount)
fluid.setMixingRule("HV", "UNIFAC_UMRPRU")

for run_number in (1, 2):
    test_fluid = fluid.clone()
    simulation = neqsim.pvtsimulation.simulation.SaturationTemperature(test_fluid)
    started = time.perf_counter()
    simulation.run()
    print(run_number, time.perf_counter() - started, test_fluid.getTemperature("C"))

The production-style amplification can be reproduced by evaluating the saturation temperature repeatedly while bisecting a heavy-component scaling factor between 0.5 and 1.5 with relative tolerance 1e-5.

Expected behavior

Retain the uppermost-crossing correctness introduced by #2219 without paying a near-global scan cost on every call. Repeated calls initialized near the previous saturation boundary should approach the performance of 3.1.5.

A possible direction is an adaptive/local bracket around the current state (or previous converged boundary), with expansion and a global-scan fallback when no safe bracket is found. Flash-count regression tests should cover both ordinary near-boundary fluids and retrograde/multiple-crossing fluids.

Actual behavior

In 3.20.0, every saturation-temperature calculation begins at 1200 K and scans downward in fixed 10 K steps. A boundary near 291 K therefore incurs approximately 90 coarse multiphase TPflash evaluations, followed by bisection and a final flash. An outer optimizer multiplies this cost across every objective evaluation.

The current tests constrain the result and require fewer than 115 flashes, but that ceiling still permits the observed 10x application regression. There is no near-boundary continuation/performance regression test.

Environment:

  • OS: Windows 11
  • Java version: Eclipse Temurin OpenJDK 25.0.4 LTS
  • NeqSim version: v3.1.5, v3.18.0, v3.20.0, plus source A/B at 25f0d281 and ec18449b4
  • Python neqsim version: not applicable; reproduced with jneqsim directly
  • jneqsim versions: 3.1.5, 3.18.0, 3.20.0

Additional context

  • First causal commit: ec18449b470cc728cc8367f6ef1d8364afadd9af (fix TPflash error (#2219)), first released in v3.12.0.
  • Partial repair: af6d5be4e368e393460caea1d91ac377aae89f2c (fix(pvt): stop saturation scan at upper boundary (#3271)), first released in v3.19.0.
  • The TP-flash correctness changes and uppermost-crossing behavior should be preserved. This report concerns the remaining search complexity and repeated-call performance.
  • Numerical output changes between releases were observed at application level, but they are not attributed to this performance bug; this issue is scoped to runtime and flash count.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions