Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/generate-stubs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,30 @@ on:
jobs:
generate-stubs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'

- name: Install dependencies
run: |
pip install poetry
poetry install --with dev

- name: Generate stubs
run: |
poetry run python scripts/generate_stubs.py

- name: Commit updated stubs
uses: stefanzweifel/git-auto-commit-action@v5
with:
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/pre-commit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
- uses: pre-commit/[email protected].0
- uses: pre-commit-ci/lite-action@v1.0.1
python-version: "3.12"
- uses: pre-commit/[email protected].1
- uses: pre-commit-ci/lite-action@v1.1.0
if: always()
21 changes: 10 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ NeqSim Python is part of the [NeqSim project](https://equinor.github.io/neqsimho
NeqSim Python is distributed as a pip package. Please read the [Prerequisites](#prerequisites).

End-users should install neqsim python with some additional packages by running

```
pip install neqsim
```
Expand Down Expand Up @@ -95,7 +96,7 @@ print(f"Compressor power: {process.get('compressor').getPower()/1e6:.2f} MW")

### 4. Direct Java Access (Full control)

Explicit process management using jneqsim - for advanced features:
Explicit process management using jneqsim - for advanced features see [neqsim java API](https://github.com/equinor/neqsim):

```python
from neqsim import jneqsim
Expand Down Expand Up @@ -125,27 +126,25 @@ print(f"Compressor power: {comp.getPower()/1e6:.2f} MW")

### Choosing an Approach

| Use Case | Recommended Approach |
|----------|---------------------|
| Learning & prototyping | Python wrappers |
| Jupyter notebooks | Python wrappers |
| Production applications | ProcessContext |
| Multiple parallel processes | ProcessContext |
| Configuration-driven design | ProcessBuilder |
| Advanced Java features | Direct Java access |
| Use Case | Recommended Approach |
| --------------------------- | -------------------- |
| Learning & prototyping | Python wrappers |
| Jupyter notebooks | Python wrappers |
| Production applications | ProcessContext |
| Multiple parallel processes | ProcessContext |
| Configuration-driven design | ProcessBuilder |
| Advanced Java features | Direct Java access |

See the [examples folder](https://github.com/equinor/neqsim-python/tree/master/examples) for more process simulation examples, including [processApproaches.py](https://github.com/equinor/neqsim-python/blob/master/examples/processApproaches.py) which demonstrates all four approaches.

### Prerequisites

Java version 8 or higher ([Java JDK](https://adoptium.net/)) needs to be installed. The Python package [JPype](https://github.com/jpype-project/jpype) is used to connect Python and Java. Read the [installation requirements for Jpype](https://jpype.readthedocs.io/en/latest/install.html). Be aware that mixing 64 bit Python with 32 bit Java and vice versa crashes on import of the jpype module. The needed Python packages are listed in the [NeqSim Python dependencies page](https://github.com/equinor/neqsim-python/network/dependencies).


## Contributing

Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests.


## Discussion forum

Questions related to neqsim can be posted in the [github discussion pages](https://github.com/equinor/neqsim/discussions).
Expand Down
8 changes: 6 additions & 2 deletions examples/beggsBrillPipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@
multiphase_fluid.setTotalFlowRate(100000.0, "kg/hr")

# Create inlet stream
inlet_stream = jneqsim.process.equipment.stream.Stream("Pipeline Inlet", multiphase_fluid)
inlet_stream = jneqsim.process.equipment.stream.Stream(
"Pipeline Inlet", multiphase_fluid
)

# Create Beggs and Brill pipeline
# This pipeline goes uphill with significant elevation change
pipe = jneqsim.process.equipment.pipeline.PipeBeggsAndBrills("Multiphase Pipeline", inlet_stream)
pipe = jneqsim.process.equipment.pipeline.PipeBeggsAndBrills(
"Multiphase Pipeline", inlet_stream
)

# Set pipeline geometry
pipe.setLength(10000.0) # 10 km length
Expand Down
14 changes: 10 additions & 4 deletions examples/ejectorProcess.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Ejector Process Simulation Example

This example demonstrates using an ejector (steam/gas jet) in NeqSim.
Ejectors are used in vacuum systems, refrigeration cycles, and
Ejectors are used in vacuum systems, refrigeration cycles, and
for mixing/boosting low-pressure gas with a high-pressure motive stream.

The ejector calculation uses a quasi one-dimensional formulation combining
Expand Down Expand Up @@ -37,10 +37,14 @@

# Create streams
motive_stream = jneqsim.process.equipment.stream.Stream("Motive Stream", motive_fluid)
suction_stream = jneqsim.process.equipment.stream.Stream("Suction Stream", suction_fluid)
suction_stream = jneqsim.process.equipment.stream.Stream(
"Suction Stream", suction_fluid
)

# Create ejector
ejector = jneqsim.process.equipment.ejector.Ejector("Gas Ejector", motive_stream, suction_stream)
ejector = jneqsim.process.equipment.ejector.Ejector(
"Gas Ejector", motive_stream, suction_stream
)

# Create process system and run
process = jneqsim.process.processmodel.ProcessSystem()
Expand Down Expand Up @@ -75,7 +79,9 @@
print(f" Flow rate: {outlet_flow:.0f} kg/hr")

# Get entrainment ratio (mass of suction per mass of motive)
entrainment_ratio = suction_fluid.getFlowRate("kg/hr") / motive_fluid.getFlowRate("kg/hr")
entrainment_ratio = suction_fluid.getFlowRate("kg/hr") / motive_fluid.getFlowRate(
"kg/hr"
)
print(f"\nEntrainment Ratio: {entrainment_ratio:.3f}")
print(f"Compression Ratio: {outlet_pressure / suction_stream.getPressure('bara'):.2f}")
print("=" * 60)
36 changes: 22 additions & 14 deletions examples/equationOfState.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
print("\n2. COMPARING EoS FOR NATURAL GAS")
print("-" * 40)


# Define composition
def create_natural_gas(eos_name):
"""Create a natural gas mixture with specified EoS."""
Expand All @@ -68,6 +69,7 @@ def create_natural_gas(eos_name):
gas.setMixingRule("classic")
return gas


# Conditions
temp_c = 25.0
pressure_bara = 100.0
Expand All @@ -85,16 +87,16 @@ def create_natural_gas(eos_name):
TPflash(gas)
gas.initThermoProperties()
gas.initPhysicalProperties()

z = gas.getZ()
rho = gas.getDensity("kg/m3")
cp = gas.getCp("J/molK")

if gas.hasPhaseType("gas"):
sos = gas.getPhase("gas").getSoundSpeed()
else:
sos = gas.getPhase(0).getSoundSpeed()

print(f"{eos:11} | {z:9.5f} | {rho:9.2f} | {cp:9.2f} | {sos:8.1f}")
except Exception as e:
print(f"{eos:11} | Error: {e}")
Expand All @@ -106,6 +108,7 @@ def create_natural_gas(eos_name):
print("-" * 40)
print("CPA handles hydrogen bonding (association) in water and alcohols")


def create_wet_gas(eos_name):
"""Create a wet gas mixture with specified EoS."""
if eos_name == "cpa":
Expand All @@ -114,13 +117,14 @@ def create_wet_gas(eos_name):
else:
gas = fluid(eos_name)
gas.setMixingRule("classic")

gas.addComponent("methane", 90.0, "mol%")
gas.addComponent("ethane", 5.0, "mol%")
gas.addComponent("water", 5.0, "mol%")
gas.setMultiPhaseCheck(True)
return gas


print(f"\nConditions: T = 25°C, P = 50 bara")
print("Wet natural gas with 5 mol% water")
print("\nEoS | # Phases | Gas Density | Water in Gas Phase")
Expand All @@ -134,9 +138,9 @@ def create_wet_gas(eos_name):
gas.setPressure(50.0, "bara")
TPflash(gas)
gas.initThermoProperties()

n_phases = gas.getNumberOfPhases()

if gas.hasPhaseType("gas"):
gas_phase = gas.getPhase("gas")
rho = gas_phase.getDensity("kg/m3")
Expand All @@ -146,7 +150,7 @@ def create_wet_gas(eos_name):
else:
rho = gas.getPhase(0).getDensity("kg/m3")
water_in_gas = 0.0

print(f"{eos:6} | {n_phases:8} | {rho:11.2f} | {water_in_gas:.6f}")
except Exception as e:
print(f"{eos:6} | Error: {e}")
Expand All @@ -167,7 +171,7 @@ def create_wet_gas(eos_name):
heptane.setPressure(1.01325, "bara")
TPflash(heptane)
heptane.initThermoProperties()

rho = heptane.getDensity("kg/m3")
print(f"{eos.upper()}: {rho:.1f} kg/m³")

Expand All @@ -193,7 +197,7 @@ def create_wet_gas(eos_name):
co2.setPressure(80.0, "bara")
TPflash(co2)
co2.initThermoProperties()

rho = co2.getDensity("kg/m3")
z = co2.getZ()
print(f"{eos:13} | {rho:15.2f} | {z:.5f}")
Expand All @@ -205,7 +209,8 @@ def create_wet_gas(eos_name):
# =============================================================================
print("\n6. EOS-CG FOR CO2 AND COMBUSTION GASES")
print("-" * 40)
print("""
print(
"""
EOS-CG (Equation of State for Combustion Gases) is based on GERG-2008
but optimized for CO2-rich mixtures and combustion product gases.

Expand All @@ -217,7 +222,8 @@ def create_wet_gas(eos_name):
- Blue/green hydrogen with CO2

Components: CO2, N2, O2, Ar, H2O, CO, H2, H2S, SO2, CH4
""")
"""
)

# Create a typical flue gas / CCS mixture
print("Example: CO2-rich CCS mixture")
Expand All @@ -239,7 +245,7 @@ def create_wet_gas(eos_name):
ccs_gas.setPressure(100.0, "bara")
TPflash(ccs_gas)
ccs_gas.initThermoProperties()

rho = ccs_gas.getDensity("kg/m3")
z = ccs_gas.getZ()
print(f"{eos:13} | {rho:15.2f} | {z:.5f}")
Expand All @@ -254,7 +260,8 @@ def create_wet_gas(eos_name):
# =============================================================================
print("\n7. GUIDELINES FOR EoS SELECTION")
print("-" * 40)
print("""
print(
"""
Application | Recommended EoS
-------------------------------------|----------------------
Natural gas properties | GERG-2008 (most accurate)
Expand All @@ -279,5 +286,6 @@ def create_wet_gas(eos_name):
Gas hydrates | CPA with hydrate model
|
Electrolyte solutions (brine) | Electrolyte-CPA
""")
"""
)
print("=" * 70)
15 changes: 12 additions & 3 deletions examples/equipmentFactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,18 @@
# List all available equipment types
print("\n4. Available equipment types (partial list):")
equipment_types = [
"Stream", "Compressor", "Pump", "Separator", "HeatExchanger",
"ThrottlingValve", "Mixer", "Splitter", "Cooler", "Heater",
"Expander", "Pipeline"
"Stream",
"Compressor",
"Pump",
"Separator",
"HeatExchanger",
"ThrottlingValve",
"Mixer",
"Splitter",
"Cooler",
"Heater",
"Expander",
"Pipeline",
]
for eq_type in equipment_types:
print(f" - {eq_type}")
Expand Down
6 changes: 3 additions & 3 deletions examples/flareSystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Flare System Simulation Example

This example demonstrates using the Flare unit operation in NeqSim.
A flare is used to safely combust emergency relief gases, typically
A flare is used to safely combust emergency relief gases, typically
from pressure safety valves (PSV) or blowdown systems.

Features demonstrated:
Expand Down Expand Up @@ -82,9 +82,9 @@
print(f" Mass utilization: {capacity_check.getMassUtilization() * 100:.1f}%")
else:
print(f"\n✓ Flare is within design capacity")
if not float('nan') == capacity_check.getHeatUtilization():
if not float("nan") == capacity_check.getHeatUtilization():
print(f" Heat utilization: {capacity_check.getHeatUtilization() * 100:.1f}%")
if not float('nan') == capacity_check.getMassUtilization():
if not float("nan") == capacity_check.getMassUtilization():
print(f" Mass utilization: {capacity_check.getMassUtilization() * 100:.1f}%")

print("=" * 60)
14 changes: 10 additions & 4 deletions examples/flashCalculations.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,18 @@
if natural_gas.hasPhaseType("gas"):
gas_phase = natural_gas.getPhase("gas")
print(f" Gas phase:")
print(f" - Mole fraction: {natural_gas.getMoleFraction(natural_gas.getPhaseNumberOfPhase('gas')):.4f}")
print(
f" - Mole fraction: {natural_gas.getMoleFraction(natural_gas.getPhaseNumberOfPhase('gas')):.4f}"
)
print(f" - Density: {gas_phase.getDensity('kg/m3'):.2f} kg/m³")
print(f" - Z-factor: {gas_phase.getZ():.4f}")

if natural_gas.hasPhaseType("oil"):
oil_phase = natural_gas.getPhase("oil")
print(f" Liquid phase:")
print(f" - Mole fraction: {natural_gas.getMoleFraction(natural_gas.getPhaseNumberOfPhase('oil')):.4f}")
print(
f" - Mole fraction: {natural_gas.getMoleFraction(natural_gas.getPhaseNumberOfPhase('oil')):.4f}"
)
print(f" - Density: {oil_phase.getDensity('kg/m3'):.2f} kg/m³")

# =============================================================================
Expand Down Expand Up @@ -182,7 +186,8 @@
print("\n" + "=" * 70)
print("FLASH CALCULATION SUMMARY")
print("=" * 70)
print("""
print(
"""
Flash Type | Given | Find | Application
-----------|----------------|----------------|---------------------------
TPflash | T, P | Phases, comp. | General equilibrium
Expand All @@ -191,5 +196,6 @@
TVflash | T, V | P, phases | Closed vessels
VHflash | V, H | T, P, phases | Adiabatic closed systems
VUflash | V, U | T, P, phases | Isolated systems
""")
"""
)
print("=" * 70)
Loading