import collections.abc as _abc
import logging as _logging
import os as _os
import pathlib as _pl
import pickle as _pickle
import subprocess as _subprocess
import typing as _tp
import matplotlib.pyplot as _plt
from pytrnsys_process import config as conf
from pytrnsys_process import log
from pytrnsys_process.process import data_structures as ds
[docs]
def get_sim_folders(path_to_results: _pl.Path) -> _abc.Sequence[_pl.Path]:
sim_folders = []
for item in path_to_results.glob("*"):
if item.is_dir():
sim_folders.append(item)
return sim_folders
[docs]
def get_files(
sim_folders: _abc.Sequence[_pl.Path],
results_folder_name: str = conf.global_settings.reader.folder_name_for_printer_files,
get_mfr_and_t: bool = conf.global_settings.reader.read_step_files,
read_deck_files: bool = conf.global_settings.reader.read_deck_files,
) -> _abc.Sequence[_pl.Path]:
"""Get simulation files from folders based on configuration.
Parameters
__________
sim_folders:
Sequence of paths to simulation folders
results_folder_name:
Name of folder containing printer files
get_mfr_and_t:
Whether to include step files (T and Mfr files)
read_deck_files:
Whether to include deck files
Returns
_______
Sequence of paths to simulation files
"""
sim_files: list[_pl.Path] = []
for sim_folder in sim_folders:
if get_mfr_and_t:
sim_files.extend(sim_folder.glob("*[_T,_Mfr].prt"))
if read_deck_files:
sim_files.extend(sim_folder.glob("**/*.dck"))
results_path = sim_folder / results_folder_name
if results_path.exists():
sim_files.extend(results_path.glob("*"))
return [x for x in sim_files if x.is_file()]
[docs]
def convert_svg_to_emf(file_no_suffix: _pl.Path) -> None:
logger = log.default_console_logger
try:
inkscape_path = conf.global_settings.plot.inkscape_path
if not _pl.Path(inkscape_path).exists():
raise OSError(f"Inkscape executable not found at: {inkscape_path}")
emf_filepath = file_no_suffix.with_suffix(".emf")
path_to_svg = file_no_suffix.with_suffix(".svg")
_subprocess.run(
[
inkscape_path,
"--export-filename=" + str(emf_filepath),
"--export-type=emf",
str(path_to_svg),
],
check=True,
capture_output=True,
text=True,
)
except _subprocess.CalledProcessError as e:
logger.error(
"Inkscape conversion failed: %s\nOutput: %s",
e,
e.output,
exc_info=True,
)
except OSError as e:
logger.error("System error running Inkscape: %s", e, exc_info=True)
[docs]
def get_file_content_as_string(
file_path: _pl.Path, encoding: str = "UTF-8"
) -> str:
"""Read and return the entire content of a file as a string.
Parameters
__________
file_path:
Path to the file to read
encoding:
File encoding to use. Defaults to "UTF-8".
Returns
_______
file_content: str
Content of the file as a string
"""
with open(file_path, "r", encoding=encoding) as file:
return file.read()
[docs]
def save_to_pickle(
data: ds.Simulation | ds.SimulationsData,
path: _pl.Path,
logger: _logging.Logger = log.default_console_logger,
) -> None:
"""Save ResultsForComparison data to a pickle file.
This function saves the entire ResultsForComparison object to a pickle file,
preserving all data structures and relationships.
Parameters
__________
data:
data object to save
path:
Path where to save the pickle file
Raises
_______
OSError: If there's an error when writing to the file
"""
try:
with open(path, "wb") as f:
_pickle.dump(data, f)
except OSError as e:
logger.error(
"Error saving ResultsForComparison to pickle: %s", e, exc_info=True
)
raise
[docs]
def load_simulations_data_from_pickle(
path: _pl.Path, logger: _logging.Logger = log.default_console_logger
) -> ds.SimulationsData:
"""Load SimulationsData from a pickle file.
This function loads a previously saved SimulationsData object from a pickle file.
Parameters
__________
path: pathlib.Path
To the pickle file to load
logger: logging.Logger, optional
Logger object that will log any messages, warnings, and/or errors
Returns
_______
SimulationsData: :class:`pytrnsys_processing.data_structures.SimulationsData`
Reconstructed SimulationsData object
Raises
_______
OSError: If there's an error reading the file
pickle.UnpicklingError: If the file is corrupted or invalid
"""
try:
with open(path, "rb") as f:
simulations_data = _pickle.load(f)
# Check if it has the expected attributes of SimulationsData
required_attrs = {"simulations", "scalar", "path_to_simulations"}
if all(hasattr(simulations_data, attr) for attr in required_attrs):
return _tp.cast(ds.SimulationsData, simulations_data)
raise ValueError(
f"Loaded object is missing required SimulationsData attributes. Type: {type(simulations_data).__name__}"
)
except OSError as e:
logger.error(
"Error loading ResultsForComparison from pickle: %s",
e,
exc_info=True,
)
raise
except (_pickle.UnpicklingError, ValueError) as e:
logger.error(
"Invalid ResultsForComparison pickle format: %s", e, exc_info=True
)
raise
[docs]
def load_simulation_from_pickle(
path: _pl.Path, logger: _logging.Logger = log.default_console_logger
) -> ds.Simulation:
"""Load a Simulation object from a pickle file.
This function loads a previously saved Simulation object from a pickle file.
Parameters
__________
path: pathlib.Path
To the pickle file to load
logger: logging.Logger, optional
Logger object that will log any messages, warnings, and/or errors
Returns
_______
Simulation: :class:`pytrnsys_processing.data_structures.Simulation`
Reconstructed Simulation object
Raises
_______
OSError: If there's an error reading the file
pickle.UnpicklingError: If the file is corrupted or invalid"""
try:
with open(path, "rb") as f:
simulation = _pickle.load(f)
# Check if it has the expected attributes of a Simulation
required_attrs = {"monthly", "hourly", "step", "scalar", "path"}
if all(hasattr(simulation, attr) for attr in required_attrs):
return _tp.cast(ds.Simulation, simulation)
raise ValueError(
f"Loaded object is missing required Simulation attributes. Type: {type(simulation).__name__}"
)
except OSError as e:
logger.error(
"Error loading Simulation from pickle: %s", e, exc_info=True
)
raise
except (_pickle.UnpicklingError, ValueError) as e:
logger.error("Invalid Simulation pickle format: %s", e, exc_info=True)
raise