pyfock.PBC_ring
Cyclic Boundary Condition (CBC) Ring Generator for PyFock This is a way to emulate the PBC
Simple interface for creating ring supercells from 1D periodic systems and performing systematic convergence studies with extrapolation to thermodynamic limit.
Usage: # Create ring supercells import PBC_ring ring_mol = PBC_ring.ring(unit_mol, N=10, periodicity=2.5)
# Run DFT as usual
basis = Basis(ring_mol, {'all': Basis.load(mol=ring_mol, basis_name='def2-SVP')})
auxbasis = Basis(ring_mol, {'all': Basis.load(mol=ring_mol, basis_name='def2-universal-jfit')})
dft_obj = DFT(ring_mol, basis, auxbasis, xc=[1, 7])
energy = dft_obj.scf()
# Convergence study utilities
energies = PBC_ring.convergence_study(unit_mol, ring_sizes=[8,10,12,15], periodicity=2.5)
tdl_energy = PBC_ring.extrapolate_tdl(energies)
1""" 2Cyclic Boundary Condition (CBC) Ring Generator for PyFock 3This is a way to emulate the PBC 4 5Simple interface for creating ring supercells from 1D periodic systems and 6performing systematic convergence studies with extrapolation to thermodynamic limit. 7 8Usage: 9 # Create ring supercells 10 import PBC_ring 11 ring_mol = PBC_ring.ring(unit_mol, N=10, periodicity=2.5) 12 13 # Run DFT as usual 14 basis = Basis(ring_mol, {'all': Basis.load(mol=ring_mol, basis_name='def2-SVP')}) 15 auxbasis = Basis(ring_mol, {'all': Basis.load(mol=ring_mol, basis_name='def2-universal-jfit')}) 16 dft_obj = DFT(ring_mol, basis, auxbasis, xc=[1, 7]) 17 energy = dft_obj.scf() 18 19 # Convergence study utilities 20 energies = PBC_ring.convergence_study(unit_mol, ring_sizes=[8,10,12,15], periodicity=2.5) 21 tdl_energy = PBC_ring.extrapolate_tdl(energies) 22""" 23 24import numpy as np 25import math 26import matplotlib.pyplot as plt 27from typing import List, Tuple, Dict, Optional 28from pyfock import Mol, Basis, DFT 29 30def ring(mol: Mol, N: int, periodicity: float, periodic_dir: str = 'x', 31 output_xyz: bool = True, xyz_filename: Optional[str] = None) -> Mol: 32 """ 33 Create a ring supercell from a 1D periodic unit cell. 34 35 Args: 36 mol: PyFock Mol object containing the unit cell 37 N: Number of unit cells to include in the ring 38 periodicity: Length of unit cell along periodic direction (Angstrom) 39 periodic_dir: Direction of periodicity ('x', 'y', or 'z'). Default: 'x' 40 output_xyz: Whether to save XYZ file of the ring. Default: False 41 xyz_filename: Custom filename for XYZ output. If None, auto-generates name 42 43 Returns: 44 Mol: New PyFock Mol object containing the ring structure 45 46 Example: 47 >>> unit_mol = Mol(coordfile='lih_unit.xyz') # LiH unit cell 48 >>> ring_mol = ring(unit_mol, N=10, periodicity=3.2) 49 >>> # Now use ring_mol in normal PyFock DFT workflow 50 """ 51 52 # Determine periodic direction index 53 dir_map = {'x': 0, 'y': 1, 'z': 2} 54 if periodic_dir.lower() not in dir_map: 55 raise ValueError("periodic_dir must be 'x', 'y', or 'z'") 56 periodic_idx = dir_map[periodic_dir.lower()] 57 58 # Calculate ring radius from circumference = N * periodicity 59 circumference = N * periodicity 60 radius = circumference / (2.0 * math.pi) 61 62 # Build ring coordinates 63 ring_atoms = [] 64 65 for unit_idx in range(N): 66 # Angular position for this unit 67 unit_angle = 2.0 * math.pi * unit_idx / N 68 69 # Add all atoms from this unit cell 70 for atom in mol.atoms: 71 symbol = atom[0] 72 orig_coords = np.array([atom[1], atom[2], atom[3]]) 73 74 # Position along periodic direction within unit cell 75 periodic_pos = orig_coords[periodic_idx] 76 77 # Total angle for this atom 78 atom_angle = unit_angle + (2.0 * math.pi * periodic_pos / periodicity) / N 79 80 # Ring coordinates (place periodic direction in xy-plane) 81 if periodic_idx == 0: # x-direction periodic 82 x_ring = radius * math.cos(atom_angle) 83 y_ring = radius * math.sin(atom_angle) 84 z_ring = orig_coords[2] 85 elif periodic_idx == 1: # y-direction periodic 86 x_ring = orig_coords[0] 87 y_ring = radius * math.cos(atom_angle) 88 z_ring = radius * math.sin(atom_angle) 89 else: # z-direction periodic 90 x_ring = radius * math.cos(atom_angle) 91 y_ring = orig_coords[1] 92 z_ring = radius * math.sin(atom_angle) 93 94 ring_atoms.append([symbol, x_ring, y_ring, z_ring]) 95 96 # Create new Mol object for ring 97 ring_mol = Mol(atoms=ring_atoms) 98 ring_mol.label = f"Ring_{N}units_R{radius:.2f}A" 99 100 # Save XYZ file if requested 101 if output_xyz: 102 if xyz_filename is None: 103 xyz_filename = f"ring_{N}_units" 104 ring_mol.exportXYZ(xyz_filename, 105 label=f"Ring with {N} units, R={radius:.3f} A, circumference={circumference:.3f} A") 106 print(f"Saved ring structure: {xyz_filename}") 107 108 print(f"Created ring: {N} units, {len(ring_atoms)} atoms, radius={radius:.3f} A") 109 return ring_mol 110 111 112def ring_preserve_bonds(mol: Mol, N: int, periodicity: float, target_radius: float, 113 periodic_dir: str = 'x', output_xyz: bool = True, 114 xyz_filename: Optional[str] = None) -> Mol: 115 """ 116 Create a ring supercell that preserves interatomic spacing from the unit cell. 117 118 This version prioritizes maintaining correct bond lengths over fitting exactly 119 around a complete circle. The ring may span less than or more than 2π radians. 120 121 Args: 122 mol: PyFock Mol object containing the unit cell 123 N: Number of unit cells to include in the ring 124 periodicity: Length of unit cell along periodic direction (Angstrom) 125 target_radius: Desired radius for the ring (Angstrom) 126 periodic_dir: Direction of periodicity ('x', 'y', or 'z'). Default: 'x' 127 output_xyz: Whether to save XYZ file of the ring. Default: True 128 xyz_filename: Custom filename for XYZ output. If None, auto-generates name 129 130 Returns: 131 Mol: New PyFock Mol object containing the ring structure 132 133 Example: 134 >>> unit_mol = Mol(coordfile='lih_unit.xyz') # LiH unit cell, 3.2 Å period 135 >>> # Create ring with 5 Å radius, preserving bond lengths 136 >>> ring_mol = ring_preserve_bonds(unit_mol, N=10, periodicity=3.2, target_radius=5.0) 137 >>> # Bonds remain 3.2 Å apart, but ring spans ~6.4 radians (not full 2π circle) 138 """ 139 140 # Determine periodic direction index 141 dir_map = {'x': 0, 'y': 1, 'z': 2} 142 if periodic_dir.lower() not in dir_map: 143 raise ValueError("periodic_dir must be 'x', 'y', or 'z'") 144 periodic_idx = dir_map[periodic_dir.lower()] 145 146 # Calculate angular spacing to preserve bond lengths 147 angular_spacing_per_unit = periodicity / target_radius # radians per unit cell 148 total_angle_spanned = N * angular_spacing_per_unit 149 150 # Provide feedback about circle closure 151 angle_deficit = 2.0 * math.pi - total_angle_spanned 152 if abs(angle_deficit) > 0.1: # More than ~6 degrees off 153 if angle_deficit > 0: 154 print(f"Note: Ring spans {total_angle_spanned:.3f} rad ({total_angle_spanned*180/math.pi:.1f}°)") 155 print(f" Gap of {angle_deficit:.3f} rad ({angle_deficit*180/math.pi:.1f}°) to complete circle") 156 else: 157 print(f"Note: Ring spans {total_angle_spanned:.3f} rad ({total_angle_spanned*180/math.pi:.1f}°)") 158 print(f" Overlaps by {-angle_deficit:.3f} rad ({-angle_deficit*180/math.pi:.1f}°) beyond full circle") 159 else: 160 print(f"Ring nearly closes: {total_angle_spanned:.3f} rad (~2π)") 161 162 # Build ring coordinates 163 ring_atoms = [] 164 165 for unit_idx in range(N): 166 # Angular position for this unit (preserves spacing) 167 unit_angle = angular_spacing_per_unit * unit_idx 168 169 # Add all atoms from this unit cell 170 for atom in mol.atoms: 171 symbol = atom[0] 172 orig_coords = np.array([atom[1], atom[2], atom[3]]) 173 174 # Position along periodic direction within unit cell 175 periodic_pos = orig_coords[periodic_idx] 176 177 # Total angle for this atom (includes intra-unit-cell position) 178 atom_angle = unit_angle + (periodic_pos / target_radius) 179 180 # Ring coordinates (place periodic direction in appropriate plane) 181 if periodic_idx == 0: # x-direction periodic 182 x_ring = target_radius * math.cos(atom_angle) 183 y_ring = target_radius * math.sin(atom_angle) 184 z_ring = orig_coords[2] 185 elif periodic_idx == 1: # y-direction periodic 186 x_ring = orig_coords[0] 187 y_ring = target_radius * math.cos(atom_angle) 188 z_ring = target_radius * math.sin(atom_angle) 189 else: # z-direction periodic 190 x_ring = target_radius * math.cos(atom_angle) 191 y_ring = orig_coords[1] 192 z_ring = target_radius * math.sin(atom_angle) 193 194 ring_atoms.append([symbol, x_ring, y_ring, z_ring]) 195 196 # Calculate actual circumference spanned 197 actual_circumference = total_angle_spanned * target_radius 198 199 # Create new Mol object for ring 200 ring_mol = Mol(atoms=ring_atoms) 201 ring_mol.label = f"Ring_{N}units_R{target_radius:.2f}A_bondpreserved" 202 203 # Save XYZ file if requested 204 if output_xyz: 205 if xyz_filename is None: 206 xyz_filename = f"ring_{N}units_R{target_radius:.1f}A_bonds_preserved.xyz" 207 208 ring_mol.exportXYZ(xyz_filename, 209 label=f"Bond-preserving ring: {N} units, R={target_radius:.3f} A, " 210 f"span={total_angle_spanned:.3f} rad, arc_length={actual_circumference:.3f} A") 211 print(f"Saved ring structure: {xyz_filename}") 212 213 print(f"Created bond-preserving ring: {N} units, {len(ring_atoms)} atoms") 214 print(f" Radius: {target_radius:.3f} A") 215 print(f" Arc length: {actual_circumference:.3f} A (vs {N*periodicity:.3f} A linear)") 216 print(f" Bond spacing preserved: {periodicity:.3f} A") 217 218 return ring_mol 219 220 221def suggest_radius_for_closure(N: int, periodicity: float) -> float: 222 """ 223 Suggest a radius that would give near-perfect ring closure. 224 225 Args: 226 N: Number of unit cells 227 periodicity: Unit cell length 228 229 Returns: 230 float: Suggested radius for ~perfect circle closure 231 """ 232 ideal_radius = (N * periodicity) / (2.0 * math.pi) 233 return ideal_radius 234 235 236def ring_with_closure_optimization(mol: Mol, N: int, periodicity: float, 237 target_radius: Optional[float] = None, 238 optimize_closure: bool = True, 239 periodic_dir: str = 'x', 240 output_xyz: bool = True, 241 xyz_filename: Optional[str] = None) -> Mol: 242 """ 243 Create a ring with option to optimize for perfect closure or preserve bonds. 244 245 Args: 246 mol: PyFock Mol object containing the unit cell 247 N: Number of unit cells 248 periodicity: Unit cell length (Angstrom) 249 target_radius: Desired radius. If None and optimize_closure=True, calculates optimal 250 optimize_closure: If True and target_radius=None, optimizes for perfect ring closure 251 periodic_dir: Direction of periodicity ('x', 'y', or 'z') 252 output_xyz: Whether to save XYZ file 253 xyz_filename: Custom filename for XYZ output 254 255 Returns: 256 Mol: Ring structure 257 """ 258 259 if target_radius is None: 260 if optimize_closure: 261 target_radius = suggest_radius_for_closure(N, periodicity) 262 print(f"Using closure-optimized radius: {target_radius:.3f} A") 263 else: 264 raise ValueError("Must provide target_radius if optimize_closure=False") 265 266 return ring_preserve_bonds(mol, N, periodicity, target_radius, 267 periodic_dir, output_xyz, xyz_filename)
31def ring(mol: Mol, N: int, periodicity: float, periodic_dir: str = 'x', 32 output_xyz: bool = True, xyz_filename: Optional[str] = None) -> Mol: 33 """ 34 Create a ring supercell from a 1D periodic unit cell. 35 36 Args: 37 mol: PyFock Mol object containing the unit cell 38 N: Number of unit cells to include in the ring 39 periodicity: Length of unit cell along periodic direction (Angstrom) 40 periodic_dir: Direction of periodicity ('x', 'y', or 'z'). Default: 'x' 41 output_xyz: Whether to save XYZ file of the ring. Default: False 42 xyz_filename: Custom filename for XYZ output. If None, auto-generates name 43 44 Returns: 45 Mol: New PyFock Mol object containing the ring structure 46 47 Example: 48 >>> unit_mol = Mol(coordfile='lih_unit.xyz') # LiH unit cell 49 >>> ring_mol = ring(unit_mol, N=10, periodicity=3.2) 50 >>> # Now use ring_mol in normal PyFock DFT workflow 51 """ 52 53 # Determine periodic direction index 54 dir_map = {'x': 0, 'y': 1, 'z': 2} 55 if periodic_dir.lower() not in dir_map: 56 raise ValueError("periodic_dir must be 'x', 'y', or 'z'") 57 periodic_idx = dir_map[periodic_dir.lower()] 58 59 # Calculate ring radius from circumference = N * periodicity 60 circumference = N * periodicity 61 radius = circumference / (2.0 * math.pi) 62 63 # Build ring coordinates 64 ring_atoms = [] 65 66 for unit_idx in range(N): 67 # Angular position for this unit 68 unit_angle = 2.0 * math.pi * unit_idx / N 69 70 # Add all atoms from this unit cell 71 for atom in mol.atoms: 72 symbol = atom[0] 73 orig_coords = np.array([atom[1], atom[2], atom[3]]) 74 75 # Position along periodic direction within unit cell 76 periodic_pos = orig_coords[periodic_idx] 77 78 # Total angle for this atom 79 atom_angle = unit_angle + (2.0 * math.pi * periodic_pos / periodicity) / N 80 81 # Ring coordinates (place periodic direction in xy-plane) 82 if periodic_idx == 0: # x-direction periodic 83 x_ring = radius * math.cos(atom_angle) 84 y_ring = radius * math.sin(atom_angle) 85 z_ring = orig_coords[2] 86 elif periodic_idx == 1: # y-direction periodic 87 x_ring = orig_coords[0] 88 y_ring = radius * math.cos(atom_angle) 89 z_ring = radius * math.sin(atom_angle) 90 else: # z-direction periodic 91 x_ring = radius * math.cos(atom_angle) 92 y_ring = orig_coords[1] 93 z_ring = radius * math.sin(atom_angle) 94 95 ring_atoms.append([symbol, x_ring, y_ring, z_ring]) 96 97 # Create new Mol object for ring 98 ring_mol = Mol(atoms=ring_atoms) 99 ring_mol.label = f"Ring_{N}units_R{radius:.2f}A" 100 101 # Save XYZ file if requested 102 if output_xyz: 103 if xyz_filename is None: 104 xyz_filename = f"ring_{N}_units" 105 ring_mol.exportXYZ(xyz_filename, 106 label=f"Ring with {N} units, R={radius:.3f} A, circumference={circumference:.3f} A") 107 print(f"Saved ring structure: {xyz_filename}") 108 109 print(f"Created ring: {N} units, {len(ring_atoms)} atoms, radius={radius:.3f} A") 110 return ring_mol
Create a ring supercell from a 1D periodic unit cell.
Args: mol: PyFock Mol object containing the unit cell N: Number of unit cells to include in the ring periodicity: Length of unit cell along periodic direction (Angstrom) periodic_dir: Direction of periodicity ('x', 'y', or 'z'). Default: 'x' output_xyz: Whether to save XYZ file of the ring. Default: False xyz_filename: Custom filename for XYZ output. If None, auto-generates name
Returns: Mol: New PyFock Mol object containing the ring structure
Example:
unit_mol = Mol(coordfile='lih_unit.xyz') # LiH unit cell ring_mol = ring(unit_mol, N=10, periodicity=3.2)
Now use ring_mol in normal PyFock DFT workflow
113def ring_preserve_bonds(mol: Mol, N: int, periodicity: float, target_radius: float, 114 periodic_dir: str = 'x', output_xyz: bool = True, 115 xyz_filename: Optional[str] = None) -> Mol: 116 """ 117 Create a ring supercell that preserves interatomic spacing from the unit cell. 118 119 This version prioritizes maintaining correct bond lengths over fitting exactly 120 around a complete circle. The ring may span less than or more than 2π radians. 121 122 Args: 123 mol: PyFock Mol object containing the unit cell 124 N: Number of unit cells to include in the ring 125 periodicity: Length of unit cell along periodic direction (Angstrom) 126 target_radius: Desired radius for the ring (Angstrom) 127 periodic_dir: Direction of periodicity ('x', 'y', or 'z'). Default: 'x' 128 output_xyz: Whether to save XYZ file of the ring. Default: True 129 xyz_filename: Custom filename for XYZ output. If None, auto-generates name 130 131 Returns: 132 Mol: New PyFock Mol object containing the ring structure 133 134 Example: 135 >>> unit_mol = Mol(coordfile='lih_unit.xyz') # LiH unit cell, 3.2 Å period 136 >>> # Create ring with 5 Å radius, preserving bond lengths 137 >>> ring_mol = ring_preserve_bonds(unit_mol, N=10, periodicity=3.2, target_radius=5.0) 138 >>> # Bonds remain 3.2 Å apart, but ring spans ~6.4 radians (not full 2π circle) 139 """ 140 141 # Determine periodic direction index 142 dir_map = {'x': 0, 'y': 1, 'z': 2} 143 if periodic_dir.lower() not in dir_map: 144 raise ValueError("periodic_dir must be 'x', 'y', or 'z'") 145 periodic_idx = dir_map[periodic_dir.lower()] 146 147 # Calculate angular spacing to preserve bond lengths 148 angular_spacing_per_unit = periodicity / target_radius # radians per unit cell 149 total_angle_spanned = N * angular_spacing_per_unit 150 151 # Provide feedback about circle closure 152 angle_deficit = 2.0 * math.pi - total_angle_spanned 153 if abs(angle_deficit) > 0.1: # More than ~6 degrees off 154 if angle_deficit > 0: 155 print(f"Note: Ring spans {total_angle_spanned:.3f} rad ({total_angle_spanned*180/math.pi:.1f}°)") 156 print(f" Gap of {angle_deficit:.3f} rad ({angle_deficit*180/math.pi:.1f}°) to complete circle") 157 else: 158 print(f"Note: Ring spans {total_angle_spanned:.3f} rad ({total_angle_spanned*180/math.pi:.1f}°)") 159 print(f" Overlaps by {-angle_deficit:.3f} rad ({-angle_deficit*180/math.pi:.1f}°) beyond full circle") 160 else: 161 print(f"Ring nearly closes: {total_angle_spanned:.3f} rad (~2π)") 162 163 # Build ring coordinates 164 ring_atoms = [] 165 166 for unit_idx in range(N): 167 # Angular position for this unit (preserves spacing) 168 unit_angle = angular_spacing_per_unit * unit_idx 169 170 # Add all atoms from this unit cell 171 for atom in mol.atoms: 172 symbol = atom[0] 173 orig_coords = np.array([atom[1], atom[2], atom[3]]) 174 175 # Position along periodic direction within unit cell 176 periodic_pos = orig_coords[periodic_idx] 177 178 # Total angle for this atom (includes intra-unit-cell position) 179 atom_angle = unit_angle + (periodic_pos / target_radius) 180 181 # Ring coordinates (place periodic direction in appropriate plane) 182 if periodic_idx == 0: # x-direction periodic 183 x_ring = target_radius * math.cos(atom_angle) 184 y_ring = target_radius * math.sin(atom_angle) 185 z_ring = orig_coords[2] 186 elif periodic_idx == 1: # y-direction periodic 187 x_ring = orig_coords[0] 188 y_ring = target_radius * math.cos(atom_angle) 189 z_ring = target_radius * math.sin(atom_angle) 190 else: # z-direction periodic 191 x_ring = target_radius * math.cos(atom_angle) 192 y_ring = orig_coords[1] 193 z_ring = target_radius * math.sin(atom_angle) 194 195 ring_atoms.append([symbol, x_ring, y_ring, z_ring]) 196 197 # Calculate actual circumference spanned 198 actual_circumference = total_angle_spanned * target_radius 199 200 # Create new Mol object for ring 201 ring_mol = Mol(atoms=ring_atoms) 202 ring_mol.label = f"Ring_{N}units_R{target_radius:.2f}A_bondpreserved" 203 204 # Save XYZ file if requested 205 if output_xyz: 206 if xyz_filename is None: 207 xyz_filename = f"ring_{N}units_R{target_radius:.1f}A_bonds_preserved.xyz" 208 209 ring_mol.exportXYZ(xyz_filename, 210 label=f"Bond-preserving ring: {N} units, R={target_radius:.3f} A, " 211 f"span={total_angle_spanned:.3f} rad, arc_length={actual_circumference:.3f} A") 212 print(f"Saved ring structure: {xyz_filename}") 213 214 print(f"Created bond-preserving ring: {N} units, {len(ring_atoms)} atoms") 215 print(f" Radius: {target_radius:.3f} A") 216 print(f" Arc length: {actual_circumference:.3f} A (vs {N*periodicity:.3f} A linear)") 217 print(f" Bond spacing preserved: {periodicity:.3f} A") 218 219 return ring_mol
Create a ring supercell that preserves interatomic spacing from the unit cell.
This version prioritizes maintaining correct bond lengths over fitting exactly around a complete circle. The ring may span less than or more than 2π radians.
Args: mol: PyFock Mol object containing the unit cell N: Number of unit cells to include in the ring periodicity: Length of unit cell along periodic direction (Angstrom) target_radius: Desired radius for the ring (Angstrom) periodic_dir: Direction of periodicity ('x', 'y', or 'z'). Default: 'x' output_xyz: Whether to save XYZ file of the ring. Default: True xyz_filename: Custom filename for XYZ output. If None, auto-generates name
Returns: Mol: New PyFock Mol object containing the ring structure
Example:
unit_mol = Mol(coordfile='lih_unit.xyz') # LiH unit cell, 3.2 Å period
Create ring with 5 Å radius, preserving bond lengths
ring_mol = ring_preserve_bonds(unit_mol, N=10, periodicity=3.2, target_radius=5.0)
Bonds remain 3.2 Å apart, but ring spans ~6.4 radians (not full 2π circle)
222def suggest_radius_for_closure(N: int, periodicity: float) -> float: 223 """ 224 Suggest a radius that would give near-perfect ring closure. 225 226 Args: 227 N: Number of unit cells 228 periodicity: Unit cell length 229 230 Returns: 231 float: Suggested radius for ~perfect circle closure 232 """ 233 ideal_radius = (N * periodicity) / (2.0 * math.pi) 234 return ideal_radius
Suggest a radius that would give near-perfect ring closure.
Args: N: Number of unit cells periodicity: Unit cell length
Returns: float: Suggested radius for ~perfect circle closure
237def ring_with_closure_optimization(mol: Mol, N: int, periodicity: float, 238 target_radius: Optional[float] = None, 239 optimize_closure: bool = True, 240 periodic_dir: str = 'x', 241 output_xyz: bool = True, 242 xyz_filename: Optional[str] = None) -> Mol: 243 """ 244 Create a ring with option to optimize for perfect closure or preserve bonds. 245 246 Args: 247 mol: PyFock Mol object containing the unit cell 248 N: Number of unit cells 249 periodicity: Unit cell length (Angstrom) 250 target_radius: Desired radius. If None and optimize_closure=True, calculates optimal 251 optimize_closure: If True and target_radius=None, optimizes for perfect ring closure 252 periodic_dir: Direction of periodicity ('x', 'y', or 'z') 253 output_xyz: Whether to save XYZ file 254 xyz_filename: Custom filename for XYZ output 255 256 Returns: 257 Mol: Ring structure 258 """ 259 260 if target_radius is None: 261 if optimize_closure: 262 target_radius = suggest_radius_for_closure(N, periodicity) 263 print(f"Using closure-optimized radius: {target_radius:.3f} A") 264 else: 265 raise ValueError("Must provide target_radius if optimize_closure=False") 266 267 return ring_preserve_bonds(mol, N, periodicity, target_radius, 268 periodic_dir, output_xyz, xyz_filename)
Create a ring with option to optimize for perfect closure or preserve bonds.
Args: mol: PyFock Mol object containing the unit cell N: Number of unit cells periodicity: Unit cell length (Angstrom) target_radius: Desired radius. If None and optimize_closure=True, calculates optimal optimize_closure: If True and target_radius=None, optimizes for perfect ring closure periodic_dir: Direction of periodicity ('x', 'y', or 'z') output_xyz: Whether to save XYZ file xyz_filename: Custom filename for XYZ output
Returns: Mol: Ring structure