Source code for zeroheliumkit.fem.palacer
import os
import json
from dataclasses import dataclass, asdict, field
import subprocess
import platform
[docs]
@dataclass
class ProblemConfig:
Type: str = "Driven"
Verbose: int = 2
Output: str="postpro/"
def __post_init__(self):
if self.Type not in ["Eigenmode", "Driven", "Transient", "Electrostatic", "Magnetostatic"]:
raise ValueError("Problem Type must be 'Eigenmode', 'Driven', 'Transient', 'Electrostatic', or 'Magnetostatic'.")
[docs]
@dataclass
class ModelConfig:
Refinement: dict
Mesh: str = "mesh/meshfile.msh"
L0: float = 1.0e-6
[docs]
@dataclass
class MaterialsConfig:
Attributes: list[int]
Permeability: float = 1.0
Permittivity: float = 1.0
LossTan: float | list[float] = 0.0
# Conductivity: float = 0.0
# LondonDepth: float = 0.0
[docs]
@dataclass
class PostProEnergyConfig:
Index: int=1
Attributes: list[int]=field(default_factory=lambda: [1])
[docs]
@dataclass
class PostProProbeConfigurator:
xlist: list[float]
ylist: list[float]
zlist: list[float]
config: list[dict]=field(default_factory=list)
schema: tuple=field(default_factory=tuple)
def __post_init__(self):
index = 0
self.schema = (len(self.zlist), len(self.ylist), len(self.xlist))
for z in self.zlist:
for y in self.ylist:
for x in self.xlist:
index += 1
self.config.append({
"Index": index,
"Center": [x, y, z]
})
[docs]
@dataclass
class PostProProbeConfig:
Index: int=1
Center: list[float]=field(default_factory=lambda: [0,0,0])
[docs]
@dataclass
class DomainConfig:
Materials: list
Postprocessing: dict
[docs]
@dataclass
class ElementConfig:
Attributes: list[int]
Direction: str
[docs]
@dataclass
class LumpedPortConfig:
Index: int = 1
R: float = 50.0
Excitation: bool = True
Elements: list[ElementConfig] = field(default_factory=list)
[docs]
@dataclass
class ImpedanceConfig:
Attributes: list[int]
Rs: float = 0.0
Ls: float = 0.0
Cs: float = 0.0
[docs]
@dataclass
class AbsorbingConfig:
Attributes: list[int]
Order: int = 1
[docs]
@dataclass
class SurfaceCurrentElementConfig:
Attributes: list[int]
Direction: list[float]
CoordinateSystem: str="Cartesian"
[docs]
@dataclass
class SurfaceCurrentConfig:
Index: int
Elements: list[SurfaceCurrentElementConfig]
[docs]
@dataclass
class BoundaryConfig:
PEC: dict=None
Absorbing: AbsorbingConfig=None
LumpedPort: list[LumpedPortConfig]=None
Impedance: list[ImpedanceConfig]=None
SurfaceCurrent: list[SurfaceCurrentConfig]=None
def __post_init__(self):
self.__dataclass_fields__ = {
k: v for k, v in self.__dataclass_fields__.items()
if getattr(self, k) is not None
}
[docs]
@dataclass
class DrivenConfig:
MinFreq: float=1 # GHz
MaxFreq: float=10 # GHz
FreqStep: float=0.1 # GHz
Save: list=field(default_factory=lambda: [1,10])
AdaptiveTol: float=1.0e-3
[docs]
@dataclass
class EigenConfig:
N: int=6
Tol: float=1.0e-8
Target: float=5.0 # GHz
Save: int=6
[docs]
@dataclass
class SolverConfig:
Order: int = 1
Device: str = "CPU"
Driven: DrivenConfig = None
Eigenmode: EigenConfig = None
Magnetostatic: dict = None
Linear: dict = None
def __post_init__(self):
if self.Magnetostatic is not None:
self.Linear = {
"Type": "AMS",
"KSPType": "CG",
"Tol": 1.0e-6,
"MaxIts": 100
}
self.__dataclass_fields__ = {
k: v for k, v in self.__dataclass_fields__.items()
if getattr(self, k) is not None
}
[docs]
@dataclass
class PalaceConfig:
Problem: ProblemConfig
Model: ModelConfig
Domains: DomainConfig
Boundaries: BoundaryConfig
Solver: SolverConfig
[docs]
class PalaceRunner:
"""
A class to run Palace simulations.
"""
def __init__(self, json_name: str, config: PalaceConfig, exec_path: str):
self.config = config
self.exec_path = exec_path
self.json_path = self.config.Problem.Output + json_name + '.json'
self.save_json(self.json_path)
[docs]
def save_json(self, path: str = 'palace.json'):
"""
Save the Palace configuration to a JSON file.
"""
with open(path, 'w') as f:
json.dump(asdict(self.config), f, indent=2)
def run(self, multicore: int = None):
command_to_cd = f'cd {os.getcwd()}'
if multicore:
command_to_run = self.exec_path + ' -np ' + str(multicore) + ' ' + self.json_path
else:
command_to_run = self.exec_path + ' ' + self.json_path
command = command_to_cd + ' && ' + command_to_run
try:
system = platform.system()
if system == "Darwin": # macOS
# Use osascript to tell the Terminal application to open a new window and execute the command
subprocess.run([
'osascript',
'-e',
f'tell application "Terminal" to do script "{command}"'
])
elif system == "Linux": # Linux
subprocess.run([
'gnome-terminal',
'--',
'bash',
'-c',
f"{command}; exec bash"
])
elif system == "Windows": # Windows
print(" Windows system is not yet supported for automatic terminal launching.")
# need to test this part on Windows
# subprocess.run([
# 'cmd.exe',
# '/c',
# f'start cmd.exe /k "{command}"'
# ])
except KeyboardInterrupt:
message = 'Interrupted by user'
print(message)