From d8a450db76c098b54dd12562c75990cc0f0217da Mon Sep 17 00:00:00 2001 From: Tiago Pires Date: Fri, 2 Aug 2024 17:40:50 +0100 Subject: [PATCH] Fixed a bug in the abaqus interface. --- examples/abaqus_solver_fitting/config.yaml | 4 -- .../config_composite.yaml | 4 -- examples/templates/solvers/abaqus.yaml | 2 - piglot/solver/abaqus/fields.py | 13 ++-- piglot/solver/abaqus/reader.py | 70 ++++++++++++++----- piglot/solver/abaqus/solver.py | 38 ++++++---- 6 files changed, 87 insertions(+), 44 deletions(-) diff --git a/examples/abaqus_solver_fitting/config.yaml b/examples/abaqus_solver_fitting/config.yaml index 7e51008..af89804 100644 --- a/examples/abaqus_solver_fitting/config.yaml +++ b/examples/abaqus_solver_fitting/config.yaml @@ -1,15 +1,12 @@ - iters: 25 optimiser: botorch - parameters: Young: [100, 100, 300] S1: [200, 200, 400] S2: [500, 500, 700] - objective: name: fitting solver: @@ -20,7 +17,6 @@ objective: cases: 'sample.inp': step_name: Step-1 # optional field for this case - instance_name: Part-1-1 # optional field for this case fields: 'reaction_x': name: FieldsOutput diff --git a/examples/abaqus_solver_fitting/config_composite.yaml b/examples/abaqus_solver_fitting/config_composite.yaml index 5a3a04a..a0fdd72 100644 --- a/examples/abaqus_solver_fitting/config_composite.yaml +++ b/examples/abaqus_solver_fitting/config_composite.yaml @@ -1,15 +1,12 @@ - iters: 25 optimiser: botorch - parameters: Young: [100, 100, 300] S1: [200, 200, 400] S2: [500, 500, 700] - objective: name: fitting composite: True @@ -21,7 +18,6 @@ objective: cases: 'sample.inp': step_name: Step-1 # optional field for this case - instance_name: Part-1-1 # optional field for this case fields: 'reaction_x': name: FieldsOutput diff --git a/examples/templates/solvers/abaqus.yaml b/examples/templates/solvers/abaqus.yaml index d772679..5fb0e49 100644 --- a/examples/templates/solvers/abaqus.yaml +++ b/examples/templates/solvers/abaqus.yaml @@ -23,8 +23,6 @@ solver: cases: # Example for an input file 'sample.inp': - # If the input has only one job this field is optional - job_name: Job-1 # Job name to extract data # If the input has only one step this field is optional step_name: Step-1 # Step name to extract data # If the input has only one instance this field is optional diff --git a/piglot/solver/abaqus/fields.py b/piglot/solver/abaqus/fields.py index ffc8596..16aec5f 100644 --- a/piglot/solver/abaqus/fields.py +++ b/piglot/solver/abaqus/fields.py @@ -97,8 +97,9 @@ def __sanitize_field(field_name: str, field_list: List[str], keyword: str) -> st raise ValueError(f"The {keyword} name '{field_name}' not found in the file.") if field_name is None: if len(field_list) > 1: - raise ValueError(f"Multiple {keyword}s found in the file. \ - Please specify the {keyword} name.") + raise ValueError( + f"Multiple {keyword}s found in the file. Please specify the {keyword} name." + ) return field_list[0] return field_name @@ -195,12 +196,10 @@ def check(self, input_data: AbaqusInputData) -> None: input_file, ext = os.path.splitext(os.path.basename(input_data.input_file)) with open(input_file + ext, 'r', encoding='utf-8') as file: data = file.read() - if has_space: nsets_list = re.findall(r'\*Nset, nset="?([^",]+)"?', data) else: nsets_list = re.findall(r'\*Nset, nset="?([^",\s]+)"?', data) - if len(nsets_list) == 0: raise ValueError("No sets found in the file.") if self.set_name not in nsets_list: @@ -230,8 +229,10 @@ def get(self, input_data: AbaqusInputData) -> OutputResult: } # X field - field_filename = os.path.join(output_dir, - f'{input_file}_{self.set_name}_{self.x_field}.txt') + field_filename = os.path.join( + output_dir, + f'{input_file}_{self.set_name}_{self.x_field}.txt', + ) # Ensure the file exists if not os.path.exists(field_filename): return OutputResult(np.empty(0), np.empty(0)) diff --git a/piglot/solver/abaqus/reader.py b/piglot/solver/abaqus/reader.py index ae97bd0..943893b 100644 --- a/piglot/solver/abaqus/reader.py +++ b/piglot/solver/abaqus/reader.py @@ -81,8 +81,10 @@ def field_location(i, variables_array, output_variable, location): """ variable = variables_array[i] if variable in ['S', 'E', 'LE']: - location_output_variable = output_variable.getSubset(region=location, - position=ELEMENT_NODAL) + location_output_variable = output_variable.getSubset( + region=location, + position=ELEMENT_NODAL, + ) else: location_output_variable = output_variable.getSubset(region=location) return location_output_variable @@ -184,16 +186,20 @@ def write_output_file(i, variables_array, variable, step, location, file_name): component_labels = output_variable.componentLabels # Write the column headers dynamically based on the number of nodes and output # variable components - header = "Frame " + " ".join("%s_%d" % (label, v.nodeLabel) - for v in location_output_variable.values - for label in component_labels) + "\n" + header = "Frame " + " ".join( + "%s_%d" % (label, v.nodeLabel) + for v in location_output_variable.values + for label in component_labels + ) + "\n" output_file.write(header) for frame in step.frames: output_variable = frame.fieldOutputs[variable] - location_output_variable = field_location(i, - variables_array, - output_variable, - location) + location_output_variable = field_location( + i, + variables_array, + output_variable, + location, + ) output_file.write("%d " % frame.frameId) for v in location_output_variable.values: output_file.write(" ".join("%.9f" % value for value in v.data)) @@ -214,19 +220,31 @@ def find_case_insensitive_key(key_name, keys_list): Returns ------- str - The original key name from the list that matches the provided key_name, ignoring case. - - Raises - ------ - ValueError - If the key_name is not found in the keys_list, ignoring case. + The original key name from the list, if found. Otherwise, None. """ keys_list_upper = [key.upper() for key in keys_list] key_name_upper = key_name.upper() if key_name_upper not in keys_list_upper: - raise ValueError("{} not found.".format(key_name)) + return None return keys_list[keys_list_upper.index(key_name_upper)] +def sanity_check(key_name): + """ + Check if the key name is None, and raise a ValueError if it is. + + Parameters + ---------- + key_name : str + The name of the key to check. + + Raises + ------ + ValueError + Raises an error if the key name is None. + """ + if key_name is None: + raise ValueError("{} not found in the output database.".format(key_name)) + def main(): """Main function to extract the nodal data from the output database (.odb) file. """ @@ -243,14 +261,34 @@ def main(): # Create a variable that refers to the respective step step = odb.steps[find_case_insensitive_key(variables["step_name"], list(odb.steps.keys()))] + # Sanity check for step + sanity_check(step) + + # Create a variable that refers to the instance instance_name = find_case_insensitive_key( variables["instance_name"], list(odb.rootAssembly.instances.keys()), ) + # Sanity check for instance_name + sanity_check(instance_name) + + # Create a variable that refers to the node set nodeset_name = find_case_insensitive_key( variables["set_name"], list(odb.rootAssembly.instances[instance_name].nodeSets.keys()), ) + # if nodeset_name is empty, it gets the node sets of the assembly + if nodeset_name is None: + instance_name = None + nodeset_name = find_case_insensitive_key( + variables["set_name"], + list(odb.rootAssembly.nodeSets.keys()), + ) + # Sanity check for nodeset_name + sanity_check(nodeset_name) + else: + # Sanity check for nodeset_name + sanity_check(nodeset_name) node_sets = get_node_sets(instance_name, odb) diff --git a/piglot/solver/abaqus/solver.py b/piglot/solver/abaqus/solver.py index fb6cc60..b1cd5d4 100644 --- a/piglot/solver/abaqus/solver.py +++ b/piglot/solver/abaqus/solver.py @@ -128,26 +128,39 @@ def _run_case(self, values: np.ndarray, case: Case, tmp_dir: str) -> CaseResult: python_script = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'reader.py') run_odb = subprocess.run( - [self.abaqus_bin, 'viewer', f"noGUI={python_script}", "--", - f"input_file={variables['input_file']}", "--", - f"job_name={variables['job_name']}", "--", - f"step_name={variables['step_name']}", "--", - f"instance_name={variables['instance_name']}", "--", - f"set_name={variables['set_name']}", "--", - f"field={variables['field']}", "--", - f"x_field={variables['x_field']}"], + [ + self.abaqus_bin, + 'viewer', + f"noGUI={python_script}", + "--", + f"input_file={variables['input_file']}", + "--", + f"job_name={variables['job_name']}", + "--", + f"step_name={variables['step_name']}", + "--", + f"instance_name={variables['instance_name']}", + "--", + f"set_name={variables['set_name']}", + "--", + f"field={variables['field']}", + "--", + f"x_field={variables['x_field']}" + ], cwd=tmp_dir, shell=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, - check=False + check=False, ) end_time = time.time() failed_case = (run_inp.returncode != 0 or run_odb.returncode != 0) - responses = {name: field.get(input_data) if not failed_case else - OutputResult(np.empty(0), np.empty(0)) for name, field in case.fields.items()} + responses = { + name: field.get(input_data) if not failed_case + else OutputResult(np.empty(0), np.empty(0)) for name, field in case.fields.items() + } return CaseResult( begin_time, @@ -242,7 +255,8 @@ def read(config: Dict[str, Any], parameters: ParameterSet, output_dir: str) -> S for case_name, case_config in config['cases'].items(): if 'fields' not in case_config: raise ValueError( - f"Missing 'fields' in case '{case_name}' configuration.") + f"Missing 'fields' in case '{case_name}' configuration." + ) fields = { field_name: abaqus_fields_reader(field_config) for field_name, field_config in case_config['fields'].items()