Skip to content

Commit fc734cc

Browse files
Merge pull request #318 from davidrudlstorfer/restore_header_information_output
Restore header functionality of 4C input file
2 parents e897894 + ff2e7e4 commit fc734cc

9 files changed

+135
-130
lines changed

src/meshpy/core/conf.py

+4-7
Original file line numberDiff line numberDiff line change
@@ -181,14 +181,11 @@ def set_default_values(self):
181181

182182
# Lines to be added to each created input file
183183
self.input_file_meshpy_header = [
184-
"-" * 77,
184+
"-" * 40,
185185
"This input file was created with MeshPy.",
186-
"Copyright (c) 2018-2025",
187-
" Ivo Steinbrecher",
188-
" Institute for Mathematics and Computer-Based Simulation",
189-
" Universitaet der Bundeswehr Muenchen",
190-
" https://www.unibw.de/imcs-en",
191-
"-" * 77,
186+
"Copyright (c) 2018-2025 MeshPy Authors",
187+
"https://imcs-compsim.github.io/meshpy/",
188+
"-" * 40,
192189
]
193190

194191

src/meshpy/four_c/input_file.py

+110-89
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,17 @@
2323
4C."""
2424

2525
import copy as _copy
26-
import datetime as _datetime
2726
import os as _os
2827
import shutil as _shutil
2928
import subprocess as _subprocess # nosec B404
3029
import sys as _sys
30+
from datetime import datetime as _datetime
3131
from pathlib import Path as _Path
3232
from typing import Any as _Any
3333
from typing import Dict as _Dict
3434
from typing import List as _List
3535
from typing import Optional as _Optional
36+
from typing import Tuple as _Tuple
3637

3738
import yaml as _yaml
3839

@@ -346,16 +347,27 @@ def delete_section(self, section_name):
346347
raise Warning(f"Section {section_name} does not exist!")
347348

348349
def write_input_file(
349-
self, file_path: _Path, *, nox_xml_file: _Optional[str] = None, **kwargs
350+
self,
351+
file_path: _Path,
352+
*,
353+
nox_xml_file: _Optional[str] = None,
354+
add_header_default: bool = True,
355+
add_footer_application_script: bool = True,
356+
**kwargs,
350357
):
351358
"""Write the input file to disk.
352359
353360
Args:
354-
file_path: str
361+
file_path:
355362
Path to the input file that should be created.
356-
nox_xml_file: str
363+
nox_xml_file:
357364
(optional) If this argument is given, the xml file will be created
358365
with this name, in the same directory as the input file.
366+
add_header_default:
367+
Prepend the default MeshPy header comment to the input file.
368+
add_footer_application_script:
369+
Append the application script which creates the input files as a
370+
comment at the end of the input file.
359371
"""
360372

361373
# Check if a xml file needs to be written.
@@ -375,29 +387,41 @@ def write_input_file(
375387
xml_file.write(self.nox_xml)
376388

377389
with open(file_path, "w") as input_file:
390+
# write MeshPy header
391+
if add_header_default:
392+
input_file.writelines(
393+
"# " + line + "\n" for line in _mpy.input_file_meshpy_header
394+
)
395+
378396
_yaml.dump(
379397
self.get_dict_to_dump(**kwargs),
380398
input_file,
381399
Dumper=_MeshPyDumper,
382400
width=float("inf"),
383401
)
384402

403+
# Add the application script to the input file.
404+
if add_footer_application_script:
405+
application_path = _Path(_sys.argv[0]).resolve()
406+
application_script_lines = self._get_application_script(
407+
application_path
408+
)
409+
input_file.writelines(application_script_lines)
410+
385411
def get_dict_to_dump(
386412
self,
387413
*,
388-
header: bool = True,
389-
add_script_to_header: bool = True,
414+
add_header_information: bool = True,
390415
check_nox: bool = True,
391416
):
392417
"""Return the dictionary representation of this input file for dumping
393418
to a yaml file.
394419
395420
Args:
396-
header:
397-
If the header should be exported to the input file files.
398-
add_script_to_header:
399-
If true, a copy of the executing script will be added to the input
400-
file. This is only in affect when header is True.
421+
add_header_information:
422+
If the information header should be exported to the input file
423+
Contains creation date, git details of MeshPy, CubitPy and
424+
original application which created the input file if available.
401425
check_nox:
402426
If this is true, an error will be thrown if no nox file is set.
403427
"""
@@ -412,14 +436,9 @@ def get_dict_to_dump(
412436
# TODO: Check if the deepcopy makes sense to be optional
413437
yaml_dict = _copy.deepcopy(self.sections)
414438

415-
# TODO: Add header to yaml
416-
# # Add header to the input file.
417-
# end_text = None
418-
419-
# lines.extend(["// " + line for line in _mpy.input_file_meshpy_header])
420-
# if header:
421-
# header_text, end_text = self._get_header(add_script_to_header)
422-
# lines.append(header_text)
439+
# Add information header to the input file
440+
if add_header_information:
441+
yaml_dict["TITLE"] = self._get_header()
423442

424443
# Check if a file has to be created for the NOX xml information.
425444
if self.nox_xml is not None:
@@ -622,104 +641,106 @@ def get_number_of_coupling_conditions(key):
622641
_dump_mesh_items(yaml_dict, "NODE COORDS", self.nodes)
623642
_dump_mesh_items(yaml_dict, "STRUCTURE ELEMENTS", self.elements)
624643

625-
# TODO: what to do here - how to add the script
626-
# # Add end text.
627-
# if end_text is not None:
628-
# lines.append(end_text)
629-
630644
return yaml_dict
631645

632-
def _get_header(self, add_script):
633-
"""Return the header for the input file."""
646+
def _get_header(self) -> dict:
647+
"""Return the information header for the current MeshPy run.
648+
649+
Returns:
650+
A dictionary with the header information.
651+
"""
634652

635-
def get_git_data(repo):
636-
"""Return the hash and date of the current git commit."""
653+
def _get_git_data(repo_path: _Path) -> _Tuple[_Optional[str], _Optional[str]]:
654+
"""Return the hash and date of the current git commit.
655+
656+
Args:
657+
repo_path: Path to the git repository.
658+
Returns:
659+
A tuple with the hash and date of the current git commit
660+
if available, otherwise None.
661+
"""
637662
git = _shutil.which("git")
638663
if git is None:
639664
raise RuntimeError("Git executable not found")
640665
out_sha = _subprocess.run( # nosec B603
641666
[git, "rev-parse", "HEAD"],
642-
cwd=repo,
667+
cwd=repo_path,
643668
stdout=_subprocess.PIPE,
644669
stderr=_subprocess.DEVNULL,
645670
)
646671
out_date = _subprocess.run( # nosec B603
647672
[git, "show", "-s", "--format=%ci"],
648-
cwd=repo,
673+
cwd=repo_path,
649674
stdout=_subprocess.PIPE,
650675
stderr=_subprocess.DEVNULL,
651676
)
677+
652678
if not out_sha.returncode + out_date.returncode == 0:
653679
return None, None
654-
else:
655-
sha = out_sha.stdout.decode("ascii").strip()
656-
date = out_date.stdout.decode("ascii").strip()
657-
return sha, date
658-
659-
headers = []
660-
end_text = None
661-
662-
# Header containing model information.
663-
current_time_string = _datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
664-
model_header = f"// Date: {current_time_string}\n"
665-
if self.description:
666-
model_header += f"// Description: {self.description}\n"
667-
headers.append(model_header)
668-
669-
# Get information about the script.
670-
script_path = _os.path.realpath(_sys.argv[0])
671-
script_git_sha, script_git_date = get_git_data(_os.path.dirname(script_path))
672-
script_header = "// Script used to create input file:\n"
673-
script_header += f"// path: {script_path}\n"
674-
if script_git_sha is not None:
675-
script_header += (
676-
f"// git sha: {script_git_sha}\n// git date: {script_git_date}\n"
677-
)
678-
headers.append(script_header)
679680

680-
# Header containing meshpy information.
681-
meshpy_git_sha, meshpy_git_date = get_git_data(
682-
_os.path.dirname(_os.path.realpath(__file__))
681+
git_sha = out_sha.stdout.decode("ascii").strip()
682+
git_date = out_date.stdout.decode("ascii").strip()
683+
return git_sha, git_date
684+
685+
header: dict = {"MeshPy": {}}
686+
687+
header["MeshPy"]["creation_date"] = _datetime.now().isoformat(
688+
sep=" ", timespec="seconds"
683689
)
684-
headers.append(
685-
"// Input file created with meshpy\n"
686-
f"// git sha: {meshpy_git_sha}\n"
687-
f"// git date: {meshpy_git_date}\n"
690+
691+
# application which created the input file
692+
application_path = _Path(_sys.argv[0]).resolve()
693+
header["MeshPy"]["Application"] = {"path": str(application_path)}
694+
695+
application_git_sha, application_git_date = _get_git_data(
696+
application_path.parent
688697
)
698+
if application_git_sha is not None and application_git_date is not None:
699+
header["MeshPy"]["Application"].update(
700+
{
701+
"git_sha": application_git_sha,
702+
"git_date": application_git_date,
703+
}
704+
)
689705

706+
# MeshPy information
707+
meshpy_git_sha, meshpy_git_date = _get_git_data(
708+
_Path(__file__).resolve().parent
709+
)
710+
if meshpy_git_sha is not None and meshpy_git_date is not None:
711+
header["MeshPy"]["MeshPy"] = {
712+
"git_SHA": meshpy_git_sha,
713+
"git_date": meshpy_git_date,
714+
}
715+
716+
# CubitPy information
690717
if _cubitpy_is_available():
691-
# Get git information about cubitpy.
692-
cubitpy_git_sha, cubitpy_git_date = get_git_data(
718+
cubitpy_git_sha, cubitpy_git_date = _get_git_data(
693719
_os.path.dirname(_cubitpy.__file__)
694720
)
695721

696-
if cubitpy_git_sha is not None:
697-
# Cubitpy_header.
698-
headers.append(
699-
"// The module cubitpy was loaded\n"
700-
f"// git sha: {cubitpy_git_sha}\n"
701-
f"// git date: {cubitpy_git_date}\n"
702-
)
722+
if cubitpy_git_sha is not None and cubitpy_git_date is not None:
723+
header["MeshPy"]["CubitPy"] = {
724+
"git_SHA": cubitpy_git_sha,
725+
"git_date": cubitpy_git_date,
726+
}
703727

704-
string_line = "// " + "".join(["-"] * 80)
728+
return header
705729

706-
# If needed, append the contents of the script.
707-
if add_script:
708-
# Header for the script 'section'.
709-
script_lines = [
710-
string_line
711-
+ "\n// Full script used to create this input file.\n"
712-
+ string_line
713-
+ "\n"
714-
]
730+
def _get_application_script(self, application_path: _Path) -> list[str]:
731+
"""Get the script that created this input file.
732+
733+
Args:
734+
application_path: Path to the script that created this input file.
735+
Returns:
736+
A list of strings with the script that created this input file.
737+
"""
715738

716-
# Get the contents of script.
717-
with open(script_path) as script_file:
718-
script_lines.extend(script_file.readlines())
739+
application_script_lines = [
740+
"# Application script which created this input file:\n"
741+
]
719742

720-
# Comment the python code lines.
721-
end_text = "//".join(script_lines)
743+
with open(application_path) as script_file:
744+
application_script_lines.extend("# " + line for line in script_file)
722745

723-
return (
724-
string_line + "\n" + (string_line + "\n").join(headers) + string_line
725-
), end_text
746+
return application_script_lines

tests/conftest.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ def _assert_results_equal(
278278
result: Union[Path, str, dict, InputFile],
279279
rtol: Optional[float] = None,
280280
atol: Optional[float] = None,
281+
input_file_kwargs: dict = {"add_header_information": False, "check_nox": False},
281282
**kwargs,
282283
) -> None:
283284
"""Comparison between reference and result with relative or absolute
@@ -290,6 +291,8 @@ def _assert_results_equal(
290291
result: The result data.
291292
rtol: The relative tolerance.
292293
atol: The absolute tolerance.
294+
input_file_kwargs: Dictionary which contains the settings when
295+
dumping the input file.
293296
"""
294297

295298
# Per default we do a string comparison of the objects. Some data types, e.g.,
@@ -341,7 +344,7 @@ def get_dictionary(data) -> dict:
341344

342345
return yaml.safe_load(
343346
yaml.dump(
344-
data.get_dict_to_dump(check_nox=False),
347+
data.get_dict_to_dump(**input_file_kwargs),
345348
Dumper=_MeshPyDumper,
346349
width=float("inf"),
347350
)

tests/reference-files/test_abaqus_frame_normal.inp

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
** -----------------------------------------------------------------------------
1+
** ----------------------------------------
22
** This input file was created with MeshPy.
3-
** Copyright (c) 2018-2025
4-
** Ivo Steinbrecher
5-
** Institute for Mathematics and Computer-Based Simulation
6-
** Universitaet der Bundeswehr Muenchen
7-
** https://www.unibw.de/imcs-en
8-
** -----------------------------------------------------------------------------
3+
** Copyright (c) 2018-2025 MeshPy Authors
4+
** https://imcs-compsim.github.io/meshpy/
5+
** ----------------------------------------
96
*Node
107
1, 0.00000000000000e+00, 0.00000000000000e+00, 0.00000000000000e+00
118
2, 2.50000000000000e-01, 0.00000000000000e+00, 0.00000000000000e+00

tests/reference-files/test_abaqus_frame_normal_and_extra_node.inp

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
** -----------------------------------------------------------------------------
1+
** ----------------------------------------
22
** This input file was created with MeshPy.
3-
** Copyright (c) 2018-2025
4-
** Ivo Steinbrecher
5-
** Institute for Mathematics and Computer-Based Simulation
6-
** Universitaet der Bundeswehr Muenchen
7-
** https://www.unibw.de/imcs-en
8-
** -----------------------------------------------------------------------------
3+
** Copyright (c) 2018-2025 MeshPy Authors
4+
** https://imcs-compsim.github.io/meshpy/
5+
** ----------------------------------------
96
*Node
107
1, 0.00000000000000e+00, 0.00000000000000e+00, 0.00000000000000e+00
118
2, 2.50000000000000e-01, 0.00000000000000e+00, 0.00000000000000e+00

tests/reference-files/test_abaqus_helix_normal.inp

+4-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
** -----------------------------------------------------------------------------
1+
** ----------------------------------------
22
** This input file was created with MeshPy.
3-
** Copyright (c) 2018-2025
4-
** Ivo Steinbrecher
5-
** Institute for Mathematics and Computer-Based Simulation
6-
** Universitaet der Bundeswehr Muenchen
7-
** https://www.unibw.de/imcs-en
8-
** -----------------------------------------------------------------------------
3+
** Copyright (c) 2018-2025 MeshPy Authors
4+
** https://imcs-compsim.github.io/meshpy/
5+
** ----------------------------------------
96
*Node
107
1, 5.00000000000000e-01, 0.00000000000000e+00, 0.00000000000000e+00
118
2, 4.45503262094184e-01, 2.26995249869773e-01, 5.00000000000000e-02

0 commit comments

Comments
 (0)