Pedestrian (Paas 2015)#

Validation model information

  • Performed by: Nico Erlinger

  • Reviewed by: Corina Klug

Added to VIVA+ Validation Catalog on: 2022-10-21

Last modified: 2023-11-23

Model version (this notebook run for): 0.3.2

© 2019-2024, OpenVT Organization (OVTO)

Available openly under under Creative Commons Attribution 4.0 International License

References#

Experimental data#

Paas, R., Masson, C., and Davidsson, J. (2015). Head boundary conditions in pedestrian crashes with passenger cars: Six-degrees-of-freedom post-mortem human subject responses. International Journal of Crashworthiness 20, 547–559. doi: 10.1080/13588265.2015.1060731

Paas, R., Östh, J., and Davidsson, J. (2015). “Which Pragmatic Finite Element Human Body Model Scaling Technique Can Most Accurately Predict Head Impact Conditions in Pedestrian-Car Crashes?” in 2015 IRCOBI Conference Proceedings, ed. International Research Council on the Biomechanics of Injury (IRCOBI), 546–576. http://www.ircobi.org/wordpress/downloads/irc15/pdf_files/64.pdf

VIVA+ validation#

Manuscript currently under preparation

Information on the subjects/specimens#

PMHS

Sex

Height [cm]

Weight [kg]

Age [yr]

Scale factor z (height)

Scale factor xand y (weight)

PM01

m

172

69

72

0.98

0.96

PF02

f

154

47

85

0.95

0.88

PM03

m

175

88

61

1.00

1.08

PM04

m

179

81

83

1.02

1.02

PM05

m

174

68

89

0.99

0.94


Postures

PM01

PF02

PM03

PM04

PM05

Experiment [°]

pos. VIVA+ [°]

Experiment [°]

pos. VIVA+ [°]

Experiment [°]

pos. VIVA+ [°]

Experiment [°]

pos. VIVA+ [°]

Experiment [°]

pos. VIVA+ [°]

side view

Right lower leg

-10

-5

-1

-2

-12

-11

-8

-6

-3

0

Right upper leg

6

2

7

6

1

2

12

9

13

8

Left lower leg

-15

-15

-12

-11

-15

-16

-37

-33

-29

-29

Left upper leg

6

1

11

7

1

1

2

4

10

5

Right lower arm

4

8

10

7

9

11

14

12

12

18

Right upper arm

-8

-6

-2

-3

-1

-6

-5

-6

-1

-6

Left lower arm

44

40

17

17

22

23

32

30

11

13

Left upper arm

-1

-1

3

3

8

6

1

6

4

6

front view

Right lower leg

1

2

1

6

10

15

-6

-9

2

3

Right upper leg

-9

-7

0

2

8

7

1

4

4

3

Left lower leg

-8

-11

12

13

6

6

10

10

2

3

Left upper leg

-7

-9

-2

-6

4

9

1

6

-4

-5

Right lower arm

-7

-9

2

2

-10

-7

7

4

-10

-8

Right upper arm

9

9

8

7

7

8

5

8

15

12

Left lower arm

22

25

-3

1

12

11

13

17

8

7

Left upper arm

-25

-23

-8

-6

-4

-8

-5

-8

16

15

Pelvis

89

90

91

90

89

89

91

90

89

89

Upper body centreline

-1

0

1

0

1

0

0

0

0

0

Shoulder

90

89

93

90

91

90

91

90

95

90

Loading and Boundary Conditions#

The displacement-time histories from the diagrams published by Paas et al. (2015) were digitised with WebPlotDigitizer v4.4 (https://automeris.io/WebPlotDigitizer).

The setup of PM01 is shown exemplary below.

PM01

Experimental responses#

Currently only trajectories are compared

Simulation metadata#

Function for plotting#

Results#

Head impact times#

Hide code cell source
hit_array=calculate_head_impact_time(
sim_x_data_leg = ('HBM', 'HBM_Leg_Vehicle_Contact_Transducer_x_force', 'time'),
sim_y_data_leg = ('HBM', 'HBM_Leg_Vehicle_Contact_Transducer_x_force', 'force'),
sim_x_data_head = ('HBM', 'HBM_Head_Vehicle_Contact_Transducer_z_force', 'time'),
sim_y_data_head = ('HBM', 'HBM_Head_Vehicle_Contact_Transducer_z_force', 'force')
)

head_time_exp = [120, 102, 162, 120, 111]
hit_df = pd.DataFrame(hit_array, columns=['Simulation', 'First leg contact [ms]', 'First head contact [ms]', 'Simulation HIT [ms]'])
hit_df['Experimental HIT [ms]'] = np.array(head_time_exp)
display(hit_df)

create_subplots(
figure_title = 'Head and leg contact force',
sim_x_data = ('HBM', 'HBM_Leg_Vehicle_Contact_Transducer_x_force', 'time'),
sim_y_data = ('HBM', 'HBM_Leg_Vehicle_Contact_Transducer_x_force', 'force'),
sim_x_data2 = ('HBM', 'HBM_Leg_Vehicle_Contact_Transducer_z_force', 'time'),
sim_y_data2 = ('HBM', 'HBM_Leg_Vehicle_Contact_Transducer_z_force', 'force'),
sim_x_data3 = ('HBM', 'HBM_Head_Vehicle_Contact_Transducer_x_force', 'time'),
sim_y_data3 = ('HBM', 'HBM_Head_Vehicle_Contact_Transducer_x_force', 'force'),
sim_x_data4 = ('HBM', 'HBM_Head_Vehicle_Contact_Transducer_z_force', 'time'),
sim_y_data4 = ('HBM', 'HBM_Head_Vehicle_Contact_Transducer_z_force', 'force'),
sim_name1_legend = 'leg x force',
sim_name2_legend = 'leg z force',
sim_name3_legend = 'head x force',
sim_name4_legend = 'head z force',
x_label = 'Time [ms]',
y_label = 'Contact force [kN]',
vertical_line1 = list(map(float, (hit_df['First leg contact [ms]'].tolist()))),
vertical_line2 = list(map(float, (hit_df['First head contact [ms]'].tolist()))),
x_lim = [0, 150],
y_lim = [0, 22],   
)
Simulation First leg contact [ms] First head contact [ms] Simulation HIT [ms] Experimental HIT [ms]
0 PM01 3.0 113.0 110 120
1 PF02 2.0 91.0 89 102
2 PM03 3.0 134.0 131 162
3 PM04 2.0 115.0 113 120
4 PM05 2.0 117.0 115 111
../_images/77f24c94df9575ddafda4cb5a34f8f582691e6176bc3c42bf3369c5fac5f7176.png

Head COG displacements#

Hide code cell source
create_subplots(
figure_title = 'X-displacement',
offset_exp_data_x = list(map(float, (hit_df['First leg contact [ms]'].tolist()))),
cut_sim_data = list(map(float, (hit_df['First head contact [ms]'].tolist()))),
sim_x_data = ('HEAD', 'x-displacement', 'time'),
sim_y_data = ('HEAD', 'x-displacement', 'displacement'),
exp_x_data = ('Head', 'time'),
exp_y_data = ('Head', 'x-displacement'),
sim_name1_legend = 'Simulation',
exp_name1_legend = 'Experiment',
x_label = 'Time [ms]',
y_label = 'Displacement [mm]',
x_lim = [0, 200],
y_lim = [-500, 500],
filename_save = 'results/figures/VIVA+_Paas_et_al_2015_x-displacement_time.svg'    
)

create_subplots(
figure_title = 'Y-displacement',
offset_exp_data_x = list(map(float, (hit_df['First leg contact [ms]'].tolist()))),
cut_sim_data = list(map(float, (hit_df['First head contact [ms]'].tolist()))),
sim_x_data = ('HEAD', 'y-displacement', 'time'),
sim_y_data = ('HEAD', 'y-displacement', 'displacement'),
exp_x_data = ('Head', 'time'),
exp_y_data = ('Head', 'y-displacement'),
sim_name1_legend = 'Simulation',
exp_name1_legend = 'Experiment',
x_label = 'Time [ms]',
y_label = 'Displacement [mm]',
x_lim = [0, 200],
y_lim = [-500, 500],
filename_save = 'results/figures/VIVA+_Paas_et_al_2015_y-displacement_time.svg'    
)

create_subplots(
figure_title = 'Z-displacement',
offset_exp_data_x = list(map(float, (hit_df['First leg contact [ms]'].tolist()))),
cut_sim_data = list(map(float, (hit_df['First head contact [ms]'].tolist()))),
sim_x_data = ('HEAD', 'z-displacement', 'time'),
sim_y_data = ('HEAD', 'z-displacement', 'displacement'),
exp_x_data = ('Head', 'time'),
exp_y_data = ('Head', 'z-displacement'),
sim_name1_legend = 'Simulation',
exp_name1_legend = 'Experiment',
x_label = 'Time [ms]',
y_label = 'Displacement [mm]',
x_lim = [0, 200],
y_lim = [-800, 200],
filename_save = 'results/figures/VIVA+_Paas_et_al_2015_z-displacement_time.svg'    
)
../_images/ec5167bc6eb25ad66a900a6b218e0ec3d1be0f01e8d464d6b39851adfc4a4a96.png ../_images/438f58827d9450288056a713d66e3057c3ae5b7c70dbe98c027e808c0746eafd.png ../_images/756d92c5266e3ad887efc508eafc3902b9ec8e3229a726d1942a9b16274413d3.png

Tibia and femur strains#

Hide code cell source
create_subplots(
figure_title = 'Tibia strain',
sim_x_data = ('BONES', 'Tibia_Cortical_R_PS99', 'time'),
sim_y_data = ('BONES', 'Tibia_Cortical_R_PS99', 'strain'),
sim_x_data2 = ('BONES', 'Tibia_Cortical_R_MPS', 'time'),
sim_y_data2 = ('BONES', 'Tibia_Cortical_R_MPS', 'strain'),
sim_name1_legend = 'MPS',
sim_name2_legend = 'PS99',
x_label = 'Time [ms]',
y_label = 'Strain [-]',
x_lim = [0, 200],
filename_save = 'results/figures/Paas_et_al_2015_tibia_r_strain_time.svg'    
)

create_subplots(
figure_title = 'Femur strain',
sim_x_data = ('BONES', 'Femur_Cortical_R_PS99', 'time'),
sim_y_data = ('BONES', 'Femur_Cortical_R_PS99', 'strain'),
sim_x_data2 = ('BONES', 'Femur_Cortical_R_MPS', 'time'),
sim_y_data2 = ('BONES', 'Femur_Cortical_R_MPS', 'strain'),   
sim_name1_legend = 'MPS',
sim_name2_legend = 'PS99',
x_label = 'Time [ms]',
y_label = 'Strain [-]',
x_lim = [0, 200],
filename_save = 'results/figures/Paas_et_al_2015_femur_r_strain_time.svg'    
)
../_images/f9b0925405c4a7c5693d0434a8da05772423bccdf961d705a322a6efc579c078.png ../_images/ceda96ad72fd6c27122cb4e936bd82a86d5e1eddd33e60252fa3f41b056202f9.png

Energy time histories#

Hide code cell source
create_subplots(
figure_title = 'Engery time histories',
sim_x_data = ('MODEL', 'Total_Energy', 'time'),
sim_y_data = ('MODEL', 'Total_Energy', 'energy'),
sim_x_data2 = ('MODEL', 'Internal_Energy', 'time'),
sim_y_data2 = ('MODEL', 'Internal_Energy', 'energy'),
sim_x_data3 = ('MODEL', 'Kinetic_Energy', 'time'),
sim_y_data3 = ('MODEL', 'Kinetic_Energy', 'energy'),
sim_x_data4 = ('MODEL', 'Hourglass_Energy', 'time'),
sim_y_data4 = ('MODEL', 'Hourglass_Energy', 'energy'),
sim_name1_legend = 'Total energy',
sim_name2_legend = 'Interal energy',
sim_name3_legend = 'Kinetic energy',
sim_name4_legend = 'Hourglass energy',
x_label = 'Time [ms]',
y_label = 'Energy [J]',
x_lim = [0, 200],
filename_save = 'results/figures/Paas__interal_energy__kinetic_energy__time.svg'    
)
../_images/6e0946401fc4e01fc01efc552c1284aa49f9a52e4eaffc76c9ea86f3444bdc15.png

Tibia and femur fracture risk#

Hide code cell source
tibia_exp_fracture = ['no', 'yes', 'yes', 'yes', 'yes']
femur_exp_fracture = ['no', 'no', 'no', 'no', 'no']

tibia_PS99_peak_strain_array = find_peak_strains(
title = 'Tibia PS99 strain',
x_data = ('BONES', 'Tibia_Cortical_R_PS99', 'time'),
y_data = ('BONES', 'Tibia_Cortical_R_PS99', 'strain')    
)

femur_PS99_peak_strain_array = find_peak_strains(
title = 'Femur PS99 strain',
x_data = ('BONES', 'Femur_Cortical_R_PS99', 'time'),
y_data = ('BONES', 'Femur_Cortical_R_PS99', 'strain')    
)

probability_list_tibia = plot_peak_strains_on_irc(
peak_strain_array = tibia_PS99_peak_strain_array,
x_label = 'Right tibia peak PS99 [-]',
filename_strains_irc = 'data/metadata/Tibia_injury-risk-curve/strains_tibia_for_IRC.csv'
)

probability_list_femur = plot_peak_strains_on_irc(
peak_strain_array = femur_PS99_peak_strain_array,
x_label = 'Right femur peak PS99 [-]',
filename_strains_irc = 'data/metadata/Femur_injury-risk-curve/Schubert-2020_Femur_All-Strain-Curves-for-FRC.csv'
)

fracture_risks_df = pd.DataFrame({'Simulation': simulation_list,
                                'Tibia fracture probability [%]': probability_list_tibia,
                                'Experiment tibia fracture': np.array(tibia_exp_fracture), 
                                'Femur fracture probability [%]': probability_list_femur,
                                'Experiment femur fracture': np.array(femur_exp_fracture),})

display(fracture_risks_df)
Simulation Tibia fracture probability [%] Experiment tibia fracture Femur fracture probability [%] Experiment femur fracture
0 PM01 99.99 no 61.4211 no
1 PF02 86.5585 yes 99.0683 no
2 PM03 5.00817 yes 84.8492 no
3 PM04 86.2648 yes 32.0408 no
4 PM05 99.848 yes 44.7015 no
../_images/8fe333417f6820840d153ef3505ffa801a3f8d4f335bfc530104057de88af1e1.png ../_images/3399c88544c8b7099811c85ef0ecdda54b24458f23e711166b98a9222b30033c.png

ISO18571 objective rating for displacement-time histories#

Hide code cell source
from objective_rating_metrics.rating import ISO18571
def calculate_iso_score(experiment_data_file, sim_x_data, sim_y_data, exp_x_data, exp_y_data, sim_data_for_calculation_y=None,):
    ratings = []
    for i in simulation_list:
        experimental_data = pd.read_csv(experiment_data_file, delimiter=';', header=[0,1,2,3], decimal='.')
        processed_data_path = os.path.join(processed_data_dir, i).replace('\\', '/')
        simData = pd.read_csv(os.path.join(processed_data_path, dynasaur_output_file_name), 
                                    delimiter=';', na_values='-', header = [0,1,2,3]) 
              
        time_ref = np.array(experimental_data[(i,) + exp_x_data]).flatten()
        x_ref = np.array(experimental_data[(i,) + exp_y_data]).flatten()

        time_comp=np.array(simData[sim_x_data]).flatten()
        x_comp = simData[sim_y_data]
        
        if sim_data_for_calculation_y is not None:
            x_comp = x_comp + simData[sim_data_for_calculation_y]
        x_comp=np.array(x_comp).flatten() 
        
        time_comp = np.delete(time_comp,np.s_[150:])
        x_comp = np.delete(x_comp, np.s_[150:])

        ind_nan = []
        ind_nan = np.where(np.isnan(x_ref))
        
        if len(ind_nan[0]) > 0:
            x_ref = x_ref[0:ind_nan[0][0]-1]
            x_time_ref = time_ref[0:ind_nan[0][0]-1]
            x_comp = x_comp[0:ind_nan[0][0]-1]
            x_time_comp = time_comp[0:ind_nan[0][0]-1]
        else:
            x_time_ref = time_ref
            x_time_comp = time_comp
            
        if len(x_ref) > len(x_comp):
            ind_end = len(x_comp)
            x_ref = x_ref[0:ind_end]
            x_time_ref = x_time_ref[0:ind_end]
            
        elif len(x_comp) > len(x_ref):
            ind_end = len(x_ref)
            x_comp = x_comp[0:ind_end]
            x_time_comp = x_time_comp[0:ind_end]   

        ref = np.vstack((x_time_ref, x_ref)).T
        comp = np.vstack((x_time_comp, x_comp)).T

        iso_rating = ISO18571(reference_curve=ref, comparison_curve=comp)
        
        ratings.append([i,
        iso_rating.corridor_rating(), 
        iso_rating.phase_rating(),
        iso_rating.magnitude_rating(),
        iso_rating.slope_rating(),
        iso_rating.overall_rating()])

    rating_array = []
    rating_array = np.append(rating_array, ratings)
    rating_array = rating_array.transpose()
    rating_array = np.reshape(rating_array, (5,6))

    return(rating_array)


# TODO implement time offset between test data and experiment


iso_ratings_x = calculate_iso_score(
sim_x_data = ('HEAD', 'x-displacement', 'time'),
sim_y_data = ('HEAD', 'x-displacement', 'displacement'),
experiment_data_file = 'data/experiment/Paas_et_al_2015_testdata.csv',
exp_x_data = ('Head', 'time'),
exp_y_data = ('Head', 'x-displacement'),
)

iso_ratings_x_df = pd.DataFrame(iso_ratings_x, columns=['Simulation', 'Corridor', 'Phase', 'Magnitude', 'Slope', 'Overall'])
print('ISO ratings for x-displacement')
display(iso_ratings_x_df)

iso_ratings_z = calculate_iso_score(
sim_x_data = ('HEAD', 'z-displacement', 'time'),
sim_y_data = ('HEAD', 'z-displacement', 'displacement'),
experiment_data_file = 'data/experiment/Paas_et_al_2015_testdata.csv',
exp_x_data = ('Head', 'time'),
exp_y_data = ('Head', 'z-displacement'),
)

iso_ratings_z_df = pd.DataFrame(iso_ratings_z, columns=['Simulation', 'Corridor', 'Phase', 'Magnitude', 'Slope', 'Overall'])
print('ISO ratings for z-displacement')
display(iso_ratings_z_df)
ISO ratings for x-displacement
Simulation Corridor Phase Magnitude Slope Overall
0 PM01 0.354 0.876 0.139 0.656 0.476
1 PF02 0.526 0.72 0.42 0.76 0.59
2 PM03 0.617 0.6 0.634 0.529 0.599
3 PM04 0.938 0.959 0.904 0.713 0.89
4 PM05 0.753 0.591 0.955 0.851 0.781
ISO ratings for z-displacement
Simulation Corridor Phase Magnitude Slope Overall
0 PM01 0.833 0.132 0 0.471 0.454
1 PF02 0.876 0.813 0.93 0.813 0.861
2 PM03 0.777 0.2 0.742 0.637 0.626
3 PM04 0.947 0.221 0.099 0.54 0.551
4 PM05 0.856 0.682 0.98 0.948 0.865