-
Notifications
You must be signed in to change notification settings - Fork 12
5.1 Fracture Analysis
CrackPy's Fracture Analysis module (crackpy.fracture_analysis
) features tools for the automatic determination of stress intensity factors (SIFs) like
- J-integral (i.e. energy release rate)
-
$K_I$ and$K_{II}$ with the interaction integral -
$T$ -stress -
$K_F, K_R, K_S$ from the Christopher-James-Patterson (CJP) model - higher-order regular terms (HORTs)
- higher-order singular terms (HOSTs) of the Williams series using the Bueckner-Chen integral or optimization methods like ODM
from full field displacements and strains obtained by digital image correlation (DIC).
We start with an example of how to use the Fracture Analysis module to calculate stress intensity factors for a single FE-simulated nodemap.
First, we need to import
# imports
import os
from matplotlib import pyplot as plt
from crackpy.fracture_analysis.analysis import FractureAnalysis
from crackpy.fracture_analysis.data_processing import InputData, CrackTipInfo
from crackpy.fracture_analysis.line_integration import IntegralProperties
from crackpy.fracture_analysis.optimization import OptimizationProperties
from crackpy.fracture_analysis.plot import PlotSettings, Plotter
from crackpy.fracture_analysis.write import OutputWriter
from crackpy.fracture_analysis.read import OutputReader
from crackpy.structure_elements.data_files import Nodemap
from crackpy.structure_elements.material import Material
and specify the input data path as well as the output path for the results
# paths
NODEMAP_FILENAME = 'File_F_10000.0_a_0.5_B_200.0_H_200.0.txt'
NODEMAP_FOLDER = os.path.join('..', '..', 'test_data', 'simulations', 'Nodemaps')
OUT_FOLDER = 'Fracture_Analysis_FE_results'
We use a linear elastic material law (Hooke's law)
# material properties
material = Material(E=72000, nu_xy=0.33, sig_yield=350)
to calculate the stresses. For this, we use a linear elastic material model (Hooke's law). Per default, the plane stress assumtion is used.
The fracture analysis needs three main input properties:
- Crack information: crack tip position and crack angle is needed for the integral evaluation and optimization/fitting methods
- Integral properties: specifications about the integration paths and which Williams terms to calculate
- Optimization properties: specifications about the fitting domain and which Williams terms to calculate
# integration
int_props = IntegralProperties(
number_of_paths=10,
number_of_nodes=100,
bottom_offset=-0, # should not be zero for dic results
top_offset=0, # should not be zero for dic results
integral_size_left=-5,
integral_size_right=5,
integral_size_top=5,
integral_size_bottom=-5,
paths_distance_top=0.5,
paths_distance_left=0.5,
paths_distance_right=0.5,
paths_distance_bottom=0.5,
mask_tolerance=2,
buckner_williams_terms=[-1, 1, 2, 3, 4, 5]
)
# optimization
opt_props = OptimizationProperties(
angle_gap=10,
min_radius=5,
max_radius=10,
tick_size=0.01,
terms=[-1, 0, 1, 2, 3, 4, 5]
)
# crack tip
ct = CrackTipInfo(50, 0, 0, 'right')
Then, we read and preprocess the data and perform a translation of the coordinate system to the crack tip position, i.e. transforming the displacement data.
# preprocess data
nodemap = Nodemap(name=NODEMAP_FILENAME, folder=NODEMAP_FOLDER)
input_data = InputData(nodemap=nodemap)
input_data.calc_stresses(material)
input_data.transform_data(ct.crack_tip_x, ct.crack_tip_y, ct.crack_tip_angle)
This transformation is necessary to assure that the crack is standardized at coordinate
# fracture analysis
analysis = FractureAnalysis(
material=Material(),
nodemap=nodemap,
data=input_data,
crack_tip_info=ct,
integral_properties=int_props,
optimization_properties=opt_props
)
analysis.run()
In order to visualize the results, we can use the plot-module
# set colormap
plt.rcParams['image.cmap'] = 'coolwarm'
plt.rcParams['figure.dpi'] = 100
# plot
plot_sets = PlotSettings(background='eps_vm')
plotter = Plotter(path=os.path.join(OUT_FOLDER, 'plots'), fracture_analysis=analysis, plot_sets=plot_sets)
plotter.plot()
and the write module to save the results in a text file
# write
writer = OutputWriter(path=os.path.join(OUT_FOLDER, 'results'), fracture_analysis=analysis)
writer.write_header()
writer.write_results()
writer.write_json(path=os.path.join(OUT_FOLDER, 'json'))
This is the results output plot:
In the following, we apply the Fracture Analysis module to calculate SIFs for DIC-nodemaps of a whole experiment (e.g. fatigue crack growth experiment). This pipeline consists of three parts
- Preparations
- Crack detection pipeline (see also Crack detection)
- Fracture analysis pipeline
import os
from matplotlib import pyplot as plt
from crackpy.fracture_analysis.line_integration import IntegralProperties
from crackpy.fracture_analysis.optimization import OptimizationProperties
from crackpy.fracture_analysis.read import OutputReader
from crackpy.crack_detection.model import get_model
from crackpy.crack_detection.pipeline.pipeline import CrackDetectionSetup, CrackDetectionPipeline
from crackpy.fracture_analysis.pipeline import FractureAnalysisPipeline
from crackpy.fracture_analysis.plot import PlotSettings
from crackpy.structure_elements.material import Material
# Paths
DATA_PATH = os.path.join('..', '..',
'test_data', 'crack_detection', 'Nodemaps')
OUT_FOLDER = 'Fracture_Analysis_Pipeline_results'
# matplotlib settings
plt.rcParams['image.cmap'] = 'coolwarm'
plt.rcParams['figure.dpi'] = 300
# crack detectors
tip_detector = get_model('ParallelNets')
path_detector = get_model('UNetPath')
# Detection setup
det_setup = CrackDetectionSetup(
specimen_size=160,
sides=['right'],
detection_window_size=50,
detection_boundary=(0, 70, -35, 35),
start_offset=(0, 0)
)
# pipeline
cd_pipeline = CrackDetectionPipeline(
data_path=DATA_PATH,
output_path=OUT_FOLDER,
tip_detector_model=tip_detector,
path_detector_model=path_detector,
setup=det_setup
)
cd_pipeline.filter_detection_stages(max_force=15000)
cd_pipeline.run_detection()
cd_pipeline.assign_remaining_stages()
cd_pipeline.write_results('crack_info_by_nodemap.txt')
The crack detection output is written to a file named "crack_info_by_nodemap.txt". This file is used as input for the fracture analysis pipeline.
We first need to specify integral properties and optimization properties. If either of these is set to None, it will be skipped during fracture analysis.
int_props = IntegralProperties(
number_of_paths=10,
number_of_nodes=100,
integral_size_left=-5,
integral_size_right=5,
integral_size_top=5,
integral_size_bottom=-5,
paths_distance_top=0.5,
paths_distance_left=0.5,
paths_distance_right=0.5,
paths_distance_bottom=0.5,
mask_tolerance=2,
buckner_williams_terms=[-1, 1, 2, 3, 4, 5]
)
opt_props = OptimizationProperties(
angle_gap=20,
min_radius=5,
max_radius=10,
tick_size=0.01,
terms=[-1, 0, 1, 2, 3, 4, 5]
)
Remark: Notice that the top and bottom offset of the integral properties is not defined yet. It can be set automatically using the method
FractureAnalysisPipeline.find_integral_props()
!
Then, we define the material properties and plot settings:
material = Material(E=72000, nu_xy=0.33, sig_yield=350)
plot_sets = PlotSettings(background='eps_vm', min_value=0, max_value=0.0068, extend='max')
We initialize the fracture analysis pipeline
fa_pipeline = FractureAnalysisPipeline(
material=material,
nodemap_path=DATA_PATH,
input_file=os.path.join(OUT_FOLDER, 'crack_info_by_nodemap.txt'),
output_path=OUT_FOLDER,
optimization_properties=opt_props,
integral_properties=int_props,
plot_sets=plot_sets
)
Then, we use the automatic integral path finding algorithm to set the integral paths for each nodemap. Make sure to run the find_max_force_stages
first.
fa_pipeline.find_max_force_stages(max_force=15000)
fa_pipeline.find_integral_props()
Remark: The
find_integral_props()
method is a BETA version. Make sure to check if the integral paths are detected correctly!
Alternatively, the same integral properties for each nodemap can be set manually. For example,
int_props = IntegralProperties(
number_of_paths=10,
number_of_nodes=100,
top_offset=1,
bottom_offset=-1
integral_size_left=-5,
integral_size_right=5,
integral_size_top=5,
integral_size_bottom=-5,
paths_distance_top=0.5,
paths_distance_left=0.5,
paths_distance_right=0.5,
paths_distance_bottom=0.5,
mask_tolerance=2,
buckner_williams_terms=[-1, 1, 2, 3, 4, 5]
)
fa_pipeline.set_integral_props_manually(int_props)
The pipeline can now be run on multiple kernels
fa_pipeline.run(num_of_kernels=10)
The fracture analysis results saved in the txt-files folder can postprocessed into a single CSV file using our OutputReader
-class. Here is an example:
# Read results and write into CSV file
reader = OutputReader()
fa_output_path = os.path.join(OUT_FOLDER, 'txt-files')
files = os.listdir(fa_output_path)
list_of_tags = ["CJP_results", "Williams_fit_results", "SIFs_integral", "Bueckner_Chen_integral",
"Path_SIFs", "Path_Williams_a_n", "Path_Williams_b_n"]
for file in files:
for tag in list_of_tags:
reader.read_tag_data(path=fa_output_path, filename=file, tag=tag)
reader.make_csv_from_results(files="all", output_path=OUT_FOLDER, output_filename='results.csv')
reader.make_csv_from_results(files="all", filter_condition={'Force': (14900, 15100)},
output_path=OUT_FOLDER, output_filename='results_maxforce.csv')
The final output folder structure looks like this:
Fracture_Analysis_Pipeline_results
└── plots
└── Dummy2_WPXXX_DummyVersuch_2_dic_results_1_52_right.png
└── Dummy2_WPXXX_DummyVersuch_2_dic_results_1_53_right.png
└── Dummy2_WPXXX_DummyVersuch_2_dic_results_1_54_right.png
└── Dummy2_WPXXX_DummyVersuch_2_dic_results_1_55_right.png
└── txt-files
└── Dummy2_WPXXX_DummyVersuch_2_dic_results_1_52_right_Output.txt
└── Dummy2_WPXXX_DummyVersuch_2_dic_results_1_53_right_Output.txt
└── Dummy2_WPXXX_DummyVersuch_2_dic_results_1_54_right_Output.txt
└── Dummy2_WPXXX_DummyVersuch_2_dic_results_1_55_right_Output.txt
└── json-files
└── Dummy2_WPXXX_DummyVersuch_2_dic_results_1_52_right_Output.json
└── Dummy2_WPXXX_DummyVersuch_2_dic_results_1_53_right_Output.json
└── Dummy2_WPXXX_DummyVersuch_2_dic_results_1_54_right_Output.json
└── Dummy2_WPXXX_DummyVersuch_2_dic_results_1_55_right_Output.json
└── FAT_Input.txt
└── results.csv