Skip to content
Open
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
128 changes: 110 additions & 18 deletions process/models/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import logging
import math
from dataclasses import dataclass, field, fields

import numpy as np

Expand All @@ -15,6 +16,93 @@
logger = logging.getLogger(__name__)


@dataclass(frozen=True)
class VacuumSpecies:
"""Dataclass for different particle species in the vacuum system"""

nitrogen: float
deuterium_tritium: float
helium: float
deuterium_tritium_again: float


@dataclass(frozen=True)
class VacuumPump:
"""Base dataclass for vacuum pump specifications"""

name: str
"""Name of the vacuum pump type"""
volflow_pump: VacuumSpecies
"""Volumetric flow rates of the vacuum pump for different gases in m³/s"""
xmult: VacuumSpecies = field(
default_factory=lambda: VacuumSpecies(
nitrogen=1.0e0,
deuterium_tritium=0.423e0,
helium=0.378e0,
deuterium_tritium_again=0.423e0,
)
Comment on lines +37 to +43

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Are these values constant? If so, this would make more sense as an enum

)
"""Multiplier to convert conductance from gas species i to nitrogen"""
description: str = ""
"""Description of the vacuum pump"""

def species(self):
"""Yield species name, pump speed, and conductance multiplier.

Yields
------
tuple[str, float, float]
Species name, volumetric flow rate, and conductance multiplier.
"""
for species_field in fields(self.volflow_pump):
species_name = species_field.name
yield (
species_name,
getattr(self.volflow_pump, species_name),
getattr(self.xmult, species_name),
)
Comment on lines +49 to +63

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think that this species almost negates the use of a dataclass



@dataclass(frozen=True)
class TurbomolecularPump(VacuumPump):
"""Turbomolecular pump with magnetic bearing specifications

Nominal speed of 2.0 m^3/s
"""

name: str = "Turbomolecular"
volflow_pump: VacuumSpecies = field(
default_factory=lambda: VacuumSpecies(
nitrogen=1.95,
deuterium_tritium=1.8,
helium=1.8,
deuterium_tritium_again=1.8,
)
)
description: str = (
"Turbomolecular pump (magnetic bearing) with nominal speed 2.0 m³/s"
)


@dataclass(frozen=True)
class CryoPump(VacuumPump):
"""Compound cryopump specifications

Nominal speed of 10 m³/s
"""

name: str = "Cryopump"
volflow_pump: VacuumSpecies = field(
default_factory=lambda: VacuumSpecies(
nitrogen=9.0,
deuterium_tritium=25.0,
helium=5.0,
deuterium_tritium_again=25.0,
)
)
description: str = "Compound cryopump with nominal speed 10 m³/s"
Comment on lines +66 to +103

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Since these dataclasses are frozen couldn't we just make some instances of the base dataclass. E.g.

CRYO_PUMP = VacuumPump(
    "Cryopump", 
    VacuumSpecies(
        nitrogen=9.0,
        deuterium_tritium=25.0,
        helium=5.0,
        deuterium_tritium_again=25.0,
    ),
    "Compound cryopump with nominal speed 10 m³/s"
)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

My only concern would be is if we ever need to add different pumps to different systems. We would then have the same class instance declared manually everywhere instead of just having a single cryopump definition that could be called

@timothy-nunn timothy-nunn Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Because this class is frozen the same object could be used everywhere, right (it cannot should not be mutated)? Unless we are saying that the volflow_pump would be different in these different use cases?



class Vacuum(Model):
"""Module containing vacuum system routines

Expand Down Expand Up @@ -302,20 +390,16 @@ def vacuum(

thcsh = thshldi / 3.0e0

# Multiplier to convert conductance from gas species i to nitrogen
xmult = [1.0e0, 0.423e0, 0.378e0, 0.423e0]
# nitrogen, D-T, helium, D-T again

nduct = ntf * ndiv

# Speed of high-vacuum pumps (m^3/s)

# nitrogen, DT, helium, DT again
sp = (
[1.95, 1.8, 1.8, 1.8]
pump = (
TurbomolecularPump()
if VacuumPumpType(self.data.vacuum.i_vacuum_pump_type)
== VacuumPumpType.TURBOMOLECULAR
else [9.0, 25.0, 5.0, 25.0]
else CryoPump()
)

# Calculate required pumping speeds
Expand Down Expand Up @@ -403,17 +487,22 @@ def vacuum(
ceff = np.full(4, 1e-6)
d = np.full(4, 1e-6)

for i in range(4):
sss = nduct / (1.0e0 / sp[i] / pumpn + 1.0e0 / cmax * xmult[i] / xmult[imax])
pump_species = tuple(pump.species())

for i, (_, volflow_pump_species, x_multiplier) in enumerate(pump_species):
sss = nduct / (
1.0e0 / volflow_pump_species / pumpn
+ 1.0e0 / cmax * x_multiplier / pump_species[imax][2]
)
if sss > s[i]:
continue
imax = i

ccc = 2.0e0 * s[i] / nduct
pumpn1 = 1.0e0 / (sp[i] * (nduct / s[i] - 1.0e0 / ccc))
pumpn2 = 1.01e0 * s[i] / (sp[i] * nduct)
pumpn1 = 1.0e0 / (volflow_pump_species * (nduct / s[i] - 1.0e0 / ccc))
pumpn2 = 1.01e0 * s[i] / (volflow_pump_species * nduct)
pumpn = max(pumpn, pumpn1, pumpn2)
ceff[i] = 1.0e0 / (nduct / s[i] - 1.0e0 / (sp[i] * pumpn))
ceff[i] = 1.0e0 / (nduct / s[i] - 1.0e0 / (volflow_pump_species * pumpn))

# Newton's method solution for duct diameter
while True:
Expand All @@ -440,13 +529,13 @@ def vacuum(
* 1.2e0
/ (l3 + 4.0e0 / 3.0e0 * d[i] * 1.2e0)
)
cap = 119.0e0 * a1 / xmult[i]
cap = 119.0e0 * a1 / x_multiplier
dcap = 2.0e0 * cap / d[i]
c1 = 119.0e0 * a1 * k1 / xmult[i]
c1 = 119.0e0 * a1 * k1 / x_multiplier
dc1 = c1 / d[i] * (3.0e0 - k1)
c2 = 119.0e0 * a2 * k2 / xmult[i]
c2 = 119.0e0 * a2 * k2 / x_multiplier
dc2 = c2 / d[i] / 1.2e0 * (3.0e0 - k2)
c3 = 119.0e0 * a3 * k3 / xmult[i]
c3 = 119.0e0 * a3 * k3 / x_multiplier
dc3 = c3 / d[i] / 1.2e0 * (3.0e0 - k3)
cnew = 1.0e0 / (1.0e0 / cap + 1.0e0 / c1 + 1.0e0 / c2 + 1.0e0 / c3)
y = -ceff[i] + cnew
Expand Down Expand Up @@ -504,11 +593,14 @@ def vacuum(
# snet(3) - net pump speed (He) provided (m^3/s)
# snet(4) - snet(2)
snet = []
for i in range(4):
for _, pump_speed, x_multiplier in pump_species:
ceff1 = ceff[imax] * nduct
snet.append(
1.0e0
/ (1.0e0 / (ceff1 * xmult[imax] / xmult[i]) + 1.0e0 / sp[i] / pumpn)
/ (
1.0e0 / (ceff1 * pump_species[imax][2] / x_multiplier)
+ 1.0e0 / pump_speed / pumpn
)
)

# If cryopumps are used then an additional pump is required
Expand Down
Loading