Source code for viperleed.calc.classes.rparams.domain_params

"""Module domain_params of viperleed.calc.classes.rparams.

Defines the DomainParameters class. Contains of information useful
when a calculation with multiple structural domains is carried out.
This module was originally (2019-06-13) part of the rparams.py module,
refactored by Michele Riva in Oct 2023.
"""

__authors__ = (
    'Florian Kraushofer (@fkraushofer)',
    )
__copyright__ = 'Copyright (c) 2019-2025 ViPErLEED developers'
__created__ = '2023-10-23'
__license__ = 'GPLv3+'

import logging
from pathlib import Path
import shutil
from zipfile import BadZipFile

from viperleed.calc.constants import DEFAULT_TENSORS
from viperleed.calc.constants import SKIP_IN_DOMAIN_MAIN
from viperleed.calc.files import iotensors
from viperleed.calc.lib import leedbase


_LOGGER = logging.getLogger(__name__)
_DOMAIN_INPUT_FILES = (
    'PARAMETERS',
    # Plus all those that should not be in the main directory
    *SKIP_IN_DOMAIN_MAIN,
    )


[docs] class DomainParameters: """Information about one structural domain."""
[docs] def __init__(self, workdir, name): """Initialize instance. Parameters ---------- workdir : pathlike Path to the sub-directory of the main work directory where calculations for this domain are performed. name : str The name of this domain (e.g., the one defined by the user via the DOMAIN parameter). """ self.workdir = Path(workdir).resolve() self.name = name self.slab = None self.rpars = None self.refcalc_required = False
def __str__(self): """Return a string representation for this domain.""" return f'domain {self.name}' def collect_input_files(self, src): """Fetch input files from the source given by the user.""" _LOGGER.info(f'Fetching input files for {self}') if not src.is_dir() and not src.is_file(): return _collect = (self._collect_inputs_from_directory if src.is_dir() else self._collect_inputs_from_tensor_file) try: _collect(src) except (OSError, BadZipFile) as exc: raise RuntimeError('Error getting domain input files') from exc def _collect_inputs_from_directory(self, src): """Fetch input files from a user-given source directory.""" # Try first to pull the inputs from the most recent tensor file try: self._collect_inputs_from_most_recent_tensor(src) except (OSError, BadZipFile): pass else: return # No usable tensors in src; fetch inputs from src directly self.refcalc_required = True _LOGGER.info(f'No previous {DEFAULT_TENSORS} found, ' 'reference calculation is required.') may_auto_generate = {'PHASESHIFTS'} for file in _DOMAIN_INPUT_FILES: try: shutil.copy2(src / file, self.workdir) except FileNotFoundError: if file in may_auto_generate: continue _LOGGER.error(f'Required file {file} for {self} ' f'not found in origin folder {src}') raise except OSError: if file in may_auto_generate: continue _LOGGER.error(f'Error copying required file {file} for ' f'{self} from origin folder {src}') raise def _collect_inputs_from_most_recent_tensor(self, src): """Fetch the most recent tensor at `src` and copy its input files.""" tensor_index = leedbase.getMaxTensorIndex(src) if not tensor_index: raise FileNotFoundError(f'No {DEFAULT_TENSORS} at {src}') # Unpack the most recent tensor at self.workdir try: iotensors.fetch_unpacked_tensor(tensor_index, base_dir=src, target_dir=self.workdir) except (OSError, BadZipFile) as exc: _LOGGER.warning(f'Error fetching {DEFAULT_TENSORS}: {exc}') raise # Finally, pull the input files there into the domain root tensor_dir = ( self.workdir / f'{DEFAULT_TENSORS}/{DEFAULT_TENSORS}_{tensor_index:03d}' ) try: self._collect_inputs_from_tensor_folder(tensor_dir) except FileNotFoundError as exc: file = Path(exc.filename).name _LOGGER.warning(f'Input file {file} is missing in ' f'{DEFAULT_TENSORS} directory. A new ' 'reference calculation is required.') raise def _collect_inputs_from_tensor_file(self, src_zip): """Fetch input files from a user-given tensor archive.""" tensor_index = leedbase.getMaxTensorIndex(self.workdir) tensor_dir = ( self.workdir / f'{DEFAULT_TENSORS}/{DEFAULT_TENSORS}_{tensor_index + 1:03d}' ) try: iotensors.unpack_tensor_file(src_zip, tensor_dir) except (OSError, BadZipFile): _LOGGER.error(f'Failed to unpack {DEFAULT_TENSORS} ' f'for {self} from file {src_zip}') raise try: self._collect_inputs_from_tensor_folder(tensor_dir) except FileNotFoundError as exc: file = Path(exc.filename).name _LOGGER.error(f'Required file {file} for {self} not found ' f'in {DEFAULT_TENSORS} directory {tensor_dir}') raise def _collect_inputs_from_tensor_folder(self, tensor_dir): """Fetch input files from an unpacked `tensor_dir`.""" for file in (_DOMAIN_INPUT_FILES + ('IVBEAMS',)): shutil.copy2(tensor_dir / file, self.workdir)