Source code for nanohubpadre.mesh

"""
Mesh-related classes for PADRE simulations.

Provides classes for defining rectangular and triangular meshes,
including X.MESH, Y.MESH, Z.MESH, and MESH commands.
"""

from typing import Any, Dict, List, Optional, Union
from .base import PadreCommand


[docs] class XMesh(PadreCommand): """ Define X grid line locations in a rectangular mesh. Parameters ---------- node : int Node number (line number in mesh, 1-300) location : float X coordinate in microns ratio : float, optional Grid spacing ratio for interpolation (0.667-1.5 recommended) density : float, optional Exact grid density/spacing in microns (alternative to ratio) Example ------- >>> x = XMesh(node=1, location=0.0) >>> x = XMesh(node=30, location=2.0, ratio=1.2) """ command_name = "X.M"
[docs] def __init__(self, node: int, location: float, ratio: Optional[float] = None, density: Optional[float] = None): super().__init__() self.node = node self.location = location self.ratio = ratio self.density = density
[docs] def to_padre(self) -> str: params = { "N": self.node, "L": self.location, } if self.ratio is not None: params["R"] = self.ratio if self.density is not None: params["H"] = self.density return self._build_command(params)
[docs] class YMesh(PadreCommand): """ Define Y grid line locations in a rectangular mesh. Parameters ---------- node : int Node number (line number in mesh, 1-300) location : float Y coordinate in microns ratio : float, optional Grid spacing ratio for interpolation density : float, optional Exact grid density/spacing in microns Example ------- >>> y = YMesh(node=1, location=-0.04) # oxide surface >>> y = YMesh(node=20, location=1.0, ratio=1.4) """ command_name = "Y.M"
[docs] def __init__(self, node: int, location: float, ratio: Optional[float] = None, density: Optional[float] = None): super().__init__() self.node = node self.location = location self.ratio = ratio self.density = density
[docs] def to_padre(self) -> str: params = { "N": self.node, "L": self.location, } if self.ratio is not None: params["R"] = self.ratio if self.density is not None: params["H"] = self.density return self._build_command(params)
[docs] class ZMesh(PadreCommand): """ Define Z grid plane locations for 3D meshes. Parameters ---------- node : int Node number (plane number in mesh) location : float Z coordinate in microns ratio : float, optional Grid spacing ratio for interpolation density : float, optional Exact grid density/spacing in microns """ command_name = "Z.M"
[docs] def __init__(self, node: int, location: float, ratio: Optional[float] = None, density: Optional[float] = None): super().__init__() self.node = node self.location = location self.ratio = ratio self.density = density
[docs] def to_padre(self) -> str: params = { "N": self.node, "L": self.location, } if self.ratio is not None: params["R"] = self.ratio if self.density is not None: params["H"] = self.density return self._build_command(params)
[docs] class Mesh(PadreCommand): """ Define mesh configuration for PADRE simulation. The Mesh class handles rectangular mesh generation, loading previous meshes, or reading triangular meshes from external files. Parameters ---------- rectangular : bool, optional Create a rectangular mesh using X.MESH/Y.MESH lines nx : int, optional Number of nodes in x-direction (implies rectangular=True) ny : int, optional Number of nodes in y-direction nz : int, optional Number of grid planes in z-direction (default=1 for 2D) width : float, optional Width in z-dimension for 2D/3D simulations (microns) infile : str, optional Input mesh file (from previous PADRE run or tri generator) outfile : str, optional Output mesh file name previous : bool, optional Load a previously generated PADRE mesh tri : bool, optional Load mesh from tri mesh generator cylindrical : bool, optional Use cylindrical symmetry about x=xmin ascii_in : bool, optional Input file is ASCII format (default True) ascii_out : bool, optional Output file in ASCII format (default True) smooth_key : int, optional Mesh smoothing options (see PADRE manual) it_smooth : int, optional Maximum smoothing iterations hetero : bool, optional Force heterostructure data format condense : str, optional Region condensation ("all", "ins", "semi", "none") Example ------- >>> # Rectangular mesh >>> mesh = Mesh(nx=40, ny=17, outfile="mesh1.pg") >>> >>> # Load previous mesh >>> mesh = Mesh(infile="mesh1.pg") >>> >>> # 3D mesh >>> mesh = Mesh(infile="mesh2d", nz=10, width=5, outfile="mesh3d") """ command_name = "MESH"
[docs] def __init__( self, rectangular: Optional[bool] = None, nx: Optional[int] = None, ny: Optional[int] = None, nz: Optional[int] = None, width: Optional[float] = None, infile: Optional[str] = None, outfile: Optional[str] = None, previous: Optional[bool] = None, tri: Optional[bool] = None, cylindrical: bool = False, ascii_in: bool = True, ascii_out: bool = True, smooth_key: Optional[int] = None, it_smooth: Optional[int] = None, hetero: bool = False, condense: Optional[str] = None, flip_y: Optional[bool] = None, scale: Optional[int] = None, diag_flip: bool = False, pack_reg: bool = False, unpack_reg: bool = False, ): super().__init__() self.rectangular = rectangular self.nx = nx self.ny = ny self.nz = nz self.width = width self.infile = infile self.outfile = outfile self.previous = previous self.tri = tri self.cylindrical = cylindrical self.ascii_in = ascii_in self.ascii_out = ascii_out self.smooth_key = smooth_key self.it_smooth = it_smooth self.hetero = hetero self.condense = condense self.flip_y = flip_y self.scale = scale self.diag_flip = diag_flip self.pack_reg = pack_reg self.unpack_reg = unpack_reg # Mesh lines stored in order of addition self._mesh_lines: List[PadreCommand] = []
[docs] def add_x_mesh(self, node: int, location: float, ratio: Optional[float] = None, density: Optional[float] = None) -> "Mesh": """Add an X.MESH line.""" self._mesh_lines.append(XMesh(node, location, ratio, density)) return self
[docs] def add_y_mesh(self, node: int, location: float, ratio: Optional[float] = None, density: Optional[float] = None) -> "Mesh": """Add a Y.MESH line.""" self._mesh_lines.append(YMesh(node, location, ratio, density)) return self
[docs] def add_z_mesh(self, node: int, location: float, ratio: Optional[float] = None, density: Optional[float] = None) -> "Mesh": """Add a Z.MESH line.""" self._mesh_lines.append(ZMesh(node, location, ratio, density)) return self
[docs] def to_padre(self) -> str: lines = [] # Build main MESH command params = {} flags = [] # Type flags if self.rectangular or (self.nx is not None and self.ny is not None): flags.append("RECT") if self.previous: flags.append("PREV") if self.tri: flags.append("TRI") # Specs if self.nx is not None: params["NX"] = self.nx if self.ny is not None: params["NY"] = self.ny if self.nz is not None: params["NZ"] = self.nz if self.width is not None: params["WIDTH"] = self.width if self.infile: params["INF"] = self.infile if self.outfile: params["OUTF"] = self.outfile if self.cylindrical: flags.append("CYLINDRICAL") if not self.ascii_in: params["ASCII.IN"] = False if not self.ascii_out: params["ASCII.OUT"] = False # Adjustments if self.smooth_key is not None: params["SMOOTH.K"] = self.smooth_key if self.it_smooth is not None: params["IT.SMOOTH"] = self.it_smooth if self.hetero: flags.append("HETERO") if self.condense: params["CONDENSE"] = self.condense if self.flip_y is not None: params["FLIP.Y"] = self.flip_y if self.scale is not None: params["SCALE"] = self.scale if self.diag_flip: flags.append("DIAG.FLI") if self.pack_reg: flags.append("PACK.REG") if self.unpack_reg: flags.append("UNPACK.REG") lines.append(self._build_command(params, flags)) # Add mesh lines in order they were added for mesh_line in self._mesh_lines: lines.append(mesh_line.to_padre()) return "\n".join(lines)
[docs] @classmethod def rectangular_grid(cls, x_nodes: List[tuple], y_nodes: List[tuple], outfile: Optional[str] = None) -> "Mesh": """ Create a rectangular mesh from node specifications. Parameters ---------- x_nodes : list of tuples List of (node, location) or (node, location, ratio) tuples y_nodes : list of tuples List of (node, location) or (node, location, ratio) tuples outfile : str, optional Output file name Example ------- >>> mesh = Mesh.rectangular_grid( ... x_nodes=[(1, 0), (30, 2)], ... y_nodes=[(1, -0.04), (5, 0), (20, 1, 1.4)], ... outfile="mesh.pg" ... ) """ mesh = cls( nx=max(n[0] for n in x_nodes), ny=max(n[0] for n in y_nodes), outfile=outfile ) for node_spec in x_nodes: node, loc = node_spec[0], node_spec[1] ratio = node_spec[2] if len(node_spec) > 2 else None mesh.add_x_mesh(node, loc, ratio=ratio) for node_spec in y_nodes: node, loc = node_spec[0], node_spec[1] ratio = node_spec[2] if len(node_spec) > 2 else None mesh.add_y_mesh(node, loc, ratio=ratio) return mesh