Skip to content

Improve friction-theory viscosity baseline and mixing#60

Merged
EvenSol merged 1 commit intomasterfrom
codex/review-friction-theory-and-update-neqsim-methods
Apr 5, 2026
Merged

Improve friction-theory viscosity baseline and mixing#60
EvenSol merged 1 commit intomasterfrom
codex/review-friction-theory-and-update-neqsim-methods

Conversation

@EvenSol
Copy link
Copy Markdown
Owner

@EvenSol EvenSol commented Apr 5, 2026

Motivation

  • Align NeqSim friction-theory viscosity implementation with modern transport-property practice by using a proper mixture rule for the dilute baseline and including polar corrections.
  • Improve robustness and numerical safety for cases where friction-theory is invoked on non-EoS phases.

Description

  • Use Wilke mixing for the dilute-gas baseline by adding calcDiluteGasMixtureViscosityMicroPoise and replacing logarithmic averaging in FrictionTheoryViscosityMethod.calcViscosity().
  • Enrich the Chung low-density factor F_c in initChungPureComponentViscosity() to include the dipole-based term and component viscosity correction via comp.getViscosityCorrectionFactor().
  • Add explicit conversion and safety constants (MICRO_POISE_TO_PA_S, MIN_VISCOSITY_PA_S), enforce a minimum viscosity floor, and convert the micropoise result to Pa·s.
  • Add deterministic fallback behavior and a warning when friction theory is used on non-EoS phases (use localPhase.getPressure() as repulsive contribution and zero attractive contribution), and update documentation docs/physical_properties/viscosity_models.md with implementation notes and added references (Chung, Wilke).

Testing

  • Ran the unit test ./mvnw -q -Dtest=FrictionTheoryViscosityMethodTest test, which completed successfully.
  • Ran the combined tests ./mvnw -q -Dtest=FrictionTheoryViscosityMethodTest,LBCViscosityMethodTest test, which completed successfully.

Codex Task

Copilot AI review requested due to automatic review settings April 5, 2026 08:51
@EvenSol EvenSol merged commit 8c70fc3 into master Apr 5, 2026
6 of 7 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates NeqSim’s friction-theory viscosity implementation to use more standard transport-property practice for the dilute baseline (Wilke mixing) and improves robustness when the method is invoked for non-EoS phases. It also updates the viscosity model documentation with implementation notes and references.

Changes:

  • Replaced log-averaged dilute baseline with a Wilke-mixed dilute-gas baseline and added explicit unit conversion + viscosity floor.
  • Enhanced Chung low-density correction factor to include dipole contribution and component viscosity correction.
  • Added non-EoS fallback behavior (repulsive = total pressure, attractive = 0) with a warning, and updated docs accordingly.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/main/java/neqsim/physicalproperties/methods/commonphasephysicalproperties/viscosity/FrictionTheoryViscosityMethod.java Implements Wilke mixing for the baseline, enriches Chung correction, adds numerical safety constants/floor, and adds a non-EoS fallback path.
docs/physical_properties/viscosity_models.md Updates friction-theory documentation to reflect the new baseline approach and fallback behavior.

Comment on lines +199 to 216
double viscosity = 0.0;
for (int i = 0; i < localPhase.getNumberOfComponents(); i++) {
double denominator = 0.0;
for (int j = 0; j < localPhase.getNumberOfComponents(); j++) {
double phiij = Math
.pow(1.0 + Math.sqrt(pureComponentViscosity[i] / pureComponentViscosity[j])
* Math.pow(localPhase.getComponent(j).getMolarMass()
/ localPhase.getComponent(i).getMolarMass(), 0.25), 2.0)
/ Math.sqrt(8.0 * (1.0 + localPhase.getComponent(i).getMolarMass()
/ localPhase.getComponent(j).getMolarMass()));
denominator += localPhase.getComponent(j).getx() * phiij;
}
viscosity += localPhase.getComponent(i).getx() * pureComponentViscosity[i] / denominator;
}
return viscosity;
}

/**
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Wilke mixing implementation here duplicates the same double-loop/phi_ij logic used in ChungViscosityMethod.calcViscosity() (see src/main/java/neqsim/physicalproperties/methods/gasphysicalproperties/viscosity/ChungViscosityMethod.java:63-78). To avoid future drift between the two implementations, consider factoring the Wilke mixture calculation into a shared helper/utility (or reusing the existing implementation) and calling it from both places.

Suggested change
double viscosity = 0.0;
for (int i = 0; i < localPhase.getNumberOfComponents(); i++) {
double denominator = 0.0;
for (int j = 0; j < localPhase.getNumberOfComponents(); j++) {
double phiij = Math
.pow(1.0 + Math.sqrt(pureComponentViscosity[i] / pureComponentViscosity[j])
* Math.pow(localPhase.getComponent(j).getMolarMass()
/ localPhase.getComponent(i).getMolarMass(), 0.25), 2.0)
/ Math.sqrt(8.0 * (1.0 + localPhase.getComponent(i).getMolarMass()
/ localPhase.getComponent(j).getMolarMass()));
denominator += localPhase.getComponent(j).getx() * phiij;
}
viscosity += localPhase.getComponent(i).getx() * pureComponentViscosity[i] / denominator;
}
return viscosity;
}
/**
return calcWilkeMixtureViscosityMicroPoise(localPhase, pureComponentViscosity);
}
/**
* Calculate mixture viscosity in micropoise from pure-component viscosities using Wilke's
* mixing rule.
*
* @param localPhase phase containing composition and molar masses
* @param componentViscosities pure-component viscosities in micropoise, indexed by component
* number
* @return mixture viscosity in micropoise
*/
private double calcWilkeMixtureViscosityMicroPoise(PhaseInterface localPhase,
double[] componentViscosities) {
double viscosity = 0.0;
for (int i = 0; i < localPhase.getNumberOfComponents(); i++) {
double denominator = 0.0;
for (int j = 0; j < localPhase.getNumberOfComponents(); j++) {
denominator += localPhase.getComponent(j).getx()
* calcWilkePhiij(localPhase.getComponent(i), localPhase.getComponent(j),
componentViscosities[i], componentViscosities[j]);
}
viscosity += localPhase.getComponent(i).getx() * componentViscosities[i] / denominator;
}
return viscosity;
}
/**
* Calculate the Wilke interaction parameter for a pair of components.
*
* @param componentI the numerator-side component in the Wilke expression
* @param componentJ the denominator-side component in the Wilke expression
* @param viscosityI pure-component viscosity of component {@code i} in micropoise
* @param viscosityJ pure-component viscosity of component {@code j} in micropoise
* @return Wilke interaction parameter for the component pair
*/
private double calcWilkePhiij(ComponentInterface componentI, ComponentInterface componentJ,
double viscosityI, double viscosityJ) {
return Math.pow(1.0 + Math.sqrt(viscosityI / viscosityJ)
* Math.pow(componentJ.getMolarMass() / componentI.getMolarMass(), 0.25), 2.0)
/ Math.sqrt(8.0 * (1.0 + componentI.getMolarMass() / componentJ.getMolarMass()));
}
/**

Copilot uses AI. Check for mistakes.
Comment on lines 153 to +157
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
Prepulsive = localPhase.getPressure();
Pattractive = 0.0;
logger.warn(
"Friction-theory viscosity called for non-EOS phase type. Falling back to total pressure for repulsive contribution and zero attractive contribution.");
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The catch (Exception ex) here will also swallow unexpected runtime failures from getPressureRepulsive()/getPressureAttractive() and silently switch to the fallback behavior. Consider narrowing this to ClassCastException (the expected non-EoS case) and include the caught exception in the warning (e.g., as a second argument) so real defects aren’t masked and debugging remains possible.

Copilot uses AI. Check for mistakes.
Comment on lines 153 to +157
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
Prepulsive = localPhase.getPressure();
Pattractive = 0.0;
logger.warn(
"Friction-theory viscosity called for non-EOS phase type. Falling back to total pressure for repulsive contribution and zero attractive contribution.");
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New behavior: when friction theory is used on a non-EoS phase, the method now falls back to using total pressure (repulsive) and zero attractive contribution. There’s currently no unit test exercising this path (e.g., using SystemIdealGas / PhaseIdealGas) to ensure the fallback stays deterministic and returns a finite viscosity. Adding a dedicated test would prevent regressions in this safety-critical branch.

Copilot uses AI. Check for mistakes.
Comment on lines +101 to +106
**Implementation notes in NeqSim (2026 update):**
- SRK and PR friction constants are selected automatically.
- The dilute-gas baseline now uses **Wilke mixing**, which is the standard state-of-practice for mixture dilute viscosities.
- The Chung correction factor $F_c$ includes acentric factor, dipole moment, and component viscosity correction term to better handle polar species.
- If friction theory is called for a non-EoS phase, NeqSim falls back to total pressure as repulsive contribution and zero attractive contribution, with a warning in logs.

Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The friction-theory equation shown in this section includes an attractive quadratic term ($\kappa_{aa} P_a^2$), but the current FrictionTheoryViscosityMethod.calcViscosity() implementation only applies a linear attractive term and a quadratic repulsive term (no P_a^2). Since this section is being updated, consider adjusting the documented equation (or adding an explicit note) so it matches the implemented model variant in NeqSim.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants