Source code for viiapackage.analyses.helper_functions.viia_create_analysis_files_abaqus

### ===================================================================================================================
###  FUNCTION: Create the files required for the analysis in ABAQUS
### ===================================================================================================================
# Copyright ©VIIA 2024

### ===================================================================================================================
###   1. Import modules
### ===================================================================================================================

# General imports
from __future__ import annotations
import re
import os
import shutil
import copy
from pathlib import Path
from typing import TYPE_CHECKING, List, Union

# References for functions and classes in the rhdhv_fem package
from rhdhv_fem import fem_ABAQUSutils
from rhdhv_fem.fem_tools import fem_copy_file
from rhdhv_fem.analyses import Analysis

# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
    from viiapackage.viiaStatus import ViiaProject


### ===================================================================================================================
###   2. Function viia_create_analysis_files_abaqus
### ===================================================================================================================

[docs]def viia_create_analysis_files_abaqus( project: ViiaProject, analysis: Analysis, abaqus_inp_location: Union[Path, str] = None) -> List[Path]: """ Function to create or collect the files required to perform the analysis. These files are collected in the current analysis folder. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - analysis (obj): Object reference of the analysis for which the files are created / collected. - abaqus_inp_location (Path or str): Location of the ABAQUS inp-file. By default the inp-file is retrieved from the workfolder, based on VIIA naming convention. Output: - Returns list of all created files in the 'current analysis folder'. """ # Collect the files files = list() # Prepare cae-file files.append(fem_copy_file( file_to_copy=project.workfolder_location / f'{project.name}.cae', destination_folder=project.current_analysis_folder)) # Prepare jnl-file files.append(fem_copy_file( file_to_copy=project.workfolder_location / f'{project.name}.jnl', destination_folder=project.current_analysis_folder)) # Prepare envelop_results json-file files.append(fem_copy_file( file_to_copy=project.workfolder_location / f'envelop_results.json', destination_folder=project.current_analysis_folder)) # Prepare inp-file if abaqus_inp_location: if isinstance(abaqus_inp_location, str): abaqus_inp_location = Path(abaqus_inp_location) files.append(fem_copy_file( file_to_copy=abaqus_inp_location, destination_folder=project.current_analysis_folder)) else: input_name = analysis.name.replace(' - ', '-').replace(' ', '_') files.append(fem_copy_file( file_to_copy=project.workfolder_location / f'{input_name}.inp', destination_folder=project.current_analysis_folder)) # Copy material include file from software-specific module in viiaPackage software_specific_folder = project.viia_settings.project_specific_package_location / 'software_specific' / 'ABAQUS' files.append(fem_copy_file( file_to_copy=software_specific_folder / 'includes' / f'materials.include', destination_folder=project.current_analysis_folder)) # Copy abaqus_v6 env-file from software-specific module in viiaPackage files.append(fem_copy_file( file_to_copy=software_specific_folder / f'abaqus_v6.env', destination_folder=project.current_analysis_folder)) # Copy subroutines from software-specific module in viiaPackage # ABAQUS version needs to be checked, by default, it is using abaqus_version = 2020 try: with open(Path(shutil.which('abaqus')), 'r') as file: lines = file.readlines() for line in lines: if '2020' in line: abaqus_version = 2020 elif '2022' in line: abaqus_version = 2022 except: abaqus_version = 2020 files.append(shutil.copytree( src=software_specific_folder / 'subroutines' / 'element_deletion' / f'abq{abaqus_version}', dst=project.current_analysis_folder / 'subroutines')) # Copy postprocessing scripts from rhdhv_fem/abaqus_utils postprocessing_path = project.current_analysis_folder / 'postprocessing' if not os.path.exists(postprocessing_path): os.makedirs(postprocessing_path) fem_path = Path(fem_ABAQUSutils.__file__).parent for py_file in ['write_abaqus_tb_file.py', 'output_storage.py', 'postprocessor.py', 'read_section_forces.py']: files.append(fem_copy_file( file_to_copy=fem_path / 'abaqus_utils' / 'abaqus_specific' / py_file, destination_folder=postprocessing_path)) # Check required files for running analysis on server folder = project.current_analysis_folder file_lst = os.listdir(folder) required_files = ['materials.include', 'abaqus_v6.env', 'envelop_results.json', 'postprocessing', 'subroutines'] missing_files = [] required_ok = True inp_file_name = None for required_file in required_files: if required_file in file_lst: continue else: required_ok = False missing_files.append(required_file) # If analysis is explicit analysis, edit inp-file if analysis.name.split(' - ')[0] in ['A10', 'A12', 'A13', 'A15']: file_lst = os.listdir(project.current_analysis_folder) # If INP file exist inp_file_exist = False for file_name in file_lst: if file_name.lower().endswith('.inp'): inp_file_exist = True inp_file_name = file_name if not inp_file_exist: project.write_log(f"WARNING: No .inp file is found") # read materials.include with open(project.current_analysis_folder / 'materials.include', 'r') as file: lines_incl = file.read() # find all materials that are included in the materials.include file material_incl_string = r'\*Material\,\sname\=\"(.*)\"' material_incl = re.findall(material_incl_string, lines_incl) # read .inp file with open(project.current_analysis_folder / inp_file_name, 'r') as file: lines_inp = file.read() # The following loop is using Regex to search materials in the .inp file for which materials provided # in the materials.include file. The function also consider the case that multiple materials could # match from inp-file. # For one to easily understand, the idea is to delete these materials first, and later on adding the # lines to refer/call the materials.include file. for material_name in material_incl: match_string = fr'(\*Material, name=\"{material_name}\"[\s|\S]*?)((\*Material)|(\*\*))' if re.search(match_string, lines_inp) is not None: lines_inp = re.sub(match_string, r'\2', lines_inp) # The matching is finished, to keep the folder clean, results will be copied and # the original inp-file will be removed lines_inp_new = copy.deepcopy(lines_inp) os.remove(project.current_analysis_folder / inp_file_name) # Write new inp-file with open(project.current_analysis_folder / inp_file_name, 'w') as file: # As mentioned above, after clean the materials, here the lines are added to refer/call the # materials.include file at the beginning of the MATERIAL session include = re.findall(r'\*\*\sMATERIALS\s\*\*\s', lines_inp_new)[0] + \ r'\n*include, input=materials.include' lines_inp_new = re.sub(r'\*\*\sMATERIALS\s\*\*\s', include, lines_inp_new) # New .inp file is written file.write(lines_inp_new) # Create batch files if required_ok: # Create one general run.bat file under the foldr A12 run_path = project.current_analysis_folder.parent / 'run.txt' if not os.path.exists(run_path) \ and not os.path.exists(project.current_analysis_folder.parent / 'run.bat'): with open(run_path, 'w') as file: inp_name_batch = inp_file_name.split('.')[0] lines = f'::Replace [your_path] with your current path of the input file folder\n' \ f'cd [your_path]\n' \ f'::Replace [core_number] with integer of number of cores you want to use \n' \ f'call abaqus -j {inp_name_batch} cpus=[core_number] double=both -seq\n' \ f'start postprocessing.bat\n' \ f'pause\n' commented_lines = f'::You can copy the lines above to queue up all signals. \n' \ f'::Note that pause should be deleted and only needed as the last line in this case.' file.writelines(lines + commented_lines) base = os.path.splitext(run_path)[0] os.rename(run_path, base + '.bat') # Creating post_processing.bat inp_name_batch = inp_file_name.split('.')[0] post_processing_path = folder / 'postprocessing.txt' with open(post_processing_path, 'w') as file: lines = f'call abaqus python postprocessing/postprocessor.py --odb_path {inp_name_batch}.odb ' \ f'--spec_path envelop_results.json\n' \ f'pause\n' file.writelines(lines) base = os.path.splitext(post_processing_path)[0] os.rename(post_processing_path, base + '.bat') else: raise ValueError( f"ERROR: {missing_files} is/are not found, please prepare it/them in your current working folder.") # Collect all signal information all_signals = [] all_signals_length = [] for timeseries_combination in project.collections.timeseries_combinations: all_signals.append(timeseries_combination.name.split('_')[-1]) all_signals_length.append(timeseries_combination.x_direction.time_series[-1]) # Prepare the rest of the signals folders except current one, which is already created, # by copying the latest folder of current signal signals_folder_path_lst = [project.current_analysis_folder] current_signal = project.project_specific['signal']['current signal'] for signal in all_signals: if signal != current_signal: # Copy the entire folder of the latest signal created by mainscript folder = project.current_analysis_folder.parent / project.project_specific['signal'][signal] shutil.copytree( src=project.current_analysis_folder, dst=folder) # Modify the input file based on different signal # The items need to be modified include: base motion signal, corresponding signal length current_path = folder signals_folder_path_lst.append(current_path) with open(current_path / inp_file_name, 'r') as file: lines_inp = file.read() # Since only Structural nonlinear 2 session needs to be modified, to avoid possible mistake, # the session is abstract separately, called sn_2 . part_str = r'(\*\*\sSTEP: Structural nonlinear 2[\s|\S]*?)\*\* OUTPUT REQUESTS' sn_2 = re.findall(part_str, lines_inp)[0] # Get and replace the signal length signal_length = all_signals_length[all_signals.index(signal)] + 0.5 pattern_signa_length = fr"(\*Dynamic, Explicit, direct user control\n)([\s|\S]*?,\s)(\d*\.\d+?)" sn_2_new = re.sub(pattern_signa_length, fr"\g<1>\g<2>{signal_length}", sn_2) # Replace the base motion signal pattern_signal = fr"(\*Boundary, amplitude=Amp-base_signal_)(\d*)(_[x,y,z])" sn_2_new = re.sub(pattern_signal, fr"\g<1>{signal[-1]}\g<3>", sn_2_new) # To keep folder clean, the results are copied and original file is removed lines_inp_new = copy.deepcopy(lines_inp.replace(sn_2, sn_2_new)) os.remove(current_path / inp_file_name) # New inp-file is written with open(current_path / inp_file_name, 'w') as file: file.write(lines_inp_new) files.append(current_path / inp_file_name) # Return the files that are created for the analysis in the analysis folder return files
### =================================================================================================================== ### 3. End of script ### ===================================================================================================================