diff --git a/docs/source/pytacs/constraints.rst b/docs/source/pytacs/constraints.rst index 2277e897f..7ada3959b 100644 --- a/docs/source/pytacs/constraints.rst +++ b/docs/source/pytacs/constraints.rst @@ -7,4 +7,5 @@ Constraint classes adjacency dvcon panel_length + panel_width volume \ No newline at end of file diff --git a/docs/source/pytacs/panel_width.rst b/docs/source/pytacs/panel_width.rst new file mode 100644 index 000000000..938f60c78 --- /dev/null +++ b/docs/source/pytacs/panel_width.rst @@ -0,0 +1,9 @@ +PanelWidthConstraint +--------------------- +.. automodule:: tacs.constraints.panel_width + +API Reference +^^^^^^^^^^^^^ +.. autoclass:: tacs.constraints.PanelWidthConstraint + :members: + :inherited-members: diff --git a/examples/gp_panel_buckling/1_closed_form_axial_modes.py b/examples/gp_panel_buckling/1_closed_form_axial_modes.py new file mode 100644 index 000000000..c2d2822df --- /dev/null +++ b/examples/gp_panel_buckling/1_closed_form_axial_modes.py @@ -0,0 +1,70 @@ +""" +Check axial closed-form modes in GPBladeStiffenedShellConstitutive class are reasonable +@Author Sean Engelstad +@Date 05/16/2024 +""" + +import numpy as np +import matplotlib.pyplot as plt +import niceplots +from tacs import TACS, constitutive + +DEG2RAD = np.pi / 180.0 + +dtype = TACS.dtype + +# Create the orthotropic layup +ortho_prop = constitutive.MaterialProperties( + rho=1550, + specific_heat=921.096, + E1=54e3, + E2=18e3, + nu12=0.25, + G12=9e3, + G13=9e3, + G23=9e3, + Xt=2410.0, + Xc=1040.0, + Yt=73.0, + Yc=173.0, + S12=71.0, + alpha=24.0e-6, + kappa=230.0, +) +ortho_ply = constitutive.OrthotropicPly(1e-3, ortho_prop) + +# don't put in any GP models (so using closed-form solutions rn) +con = constitutive.GPBladeStiffenedShellConstitutive( + panelPly=ortho_ply, + stiffenerPly=ortho_ply, + panelLength=2.0, + stiffenerPitch=0.2, + panelThick=1.5e-2, + panelPlyAngles=np.array([0.0, 45.0, 90.0], dtype=dtype) * DEG2RAD, + panelPlyFracs=np.array([0.5, 0.3, 0.2], dtype=dtype), + stiffenerHeight=0.075, + stiffenerThick=1e-2, + stiffenerPlyAngles=np.array([0.0, 60.0], dtype=dtype) * DEG2RAD, + stiffenerPlyFracs=np.array([0.6, 0.4], dtype=dtype), + panelWidth=1.0, + flangeFraction=0.8, +) + +xi = 1.0 # 0.4 + +# get the axial loads in nondimensional space w.r.t. rho_0 +n = 500 +plt.style.use(niceplots.get_style()) +rho0_vec = np.linspace(0.5, 10.0, n) +N11cr_vec = np.zeros((n,), dtype=dtype) +for gamma in [0.0, 0.1, 0.5, 1.0]: + for i, rho0 in enumerate(rho0_vec): + N11cr_vec[i] = con.nondimCriticalGlobalAxialLoad(rho0, xi, gamma) + plt.plot(rho0_vec, N11cr_vec, label=f"gamma={gamma:.2f}") + +# plot it +plt.margins(x=0.05, y=0.05) +plt.xlabel(r"$\rho_0$") +plt.ylabel(r"$N_{11,cr}^*$") +plt.legend() +plt.savefig("1-verify.png", dpi=400) diff --git a/examples/gp_panel_buckling/1_closed_form_shear_modes.py b/examples/gp_panel_buckling/1_closed_form_shear_modes.py new file mode 100644 index 000000000..574af0187 --- /dev/null +++ b/examples/gp_panel_buckling/1_closed_form_shear_modes.py @@ -0,0 +1,71 @@ +""" +Check axial closed-form modes in GPBladeStiffenedShellConstitutive class are reasonable +@Author Sean Engelstad +@Date 05/16/2024 +""" + +import numpy as np +import matplotlib.pyplot as plt +import niceplots +from tacs import TACS, constitutive + +DEG2RAD = np.pi / 180.0 + +dtype = TACS.dtype + +# Create the orthotropic layup +ortho_prop = constitutive.MaterialProperties( + rho=1550, + specific_heat=921.096, + E1=54e3, + E2=18e3, + nu12=0.25, + G12=9e3, + G13=9e3, + G23=9e3, + Xt=2410.0, + Xc=1040.0, + Yt=73.0, + Yc=173.0, + S12=71.0, + alpha=24.0e-6, + kappa=230.0, +) +ortho_ply = constitutive.OrthotropicPly(1e-3, ortho_prop) + +# don't put in any GP models (so using closed-form solutions rn) +con = constitutive.GPBladeStiffenedShellConstitutive( + panelPly=ortho_ply, + stiffenerPly=ortho_ply, + panelLength=2.0, + stiffenerPitch=0.2, + panelThick=1.5e-2, + panelPlyAngles=np.array([0.0, 45.0, 90.0], dtype=dtype) * DEG2RAD, + panelPlyFracs=np.array([0.5, 0.3, 0.2], dtype=dtype), + stiffenerHeight=0.075, + stiffenerThick=1e-2, + stiffenerPlyAngles=np.array([0.0, 60.0], dtype=dtype) * DEG2RAD, + stiffenerPlyFracs=np.array([0.6, 0.4], dtype=dtype), + panelWidth=1.0, + flangeFraction=0.8, +) +# Set the KS weight really low so that all failure modes make a +# significant contribution to the failure function derivatives +con.setKSWeight(20.0) + +# get the axial loads in nondimensional space w.r.t. rho_0 +n = 500 +plt.style.use(niceplots.get_style()) +rho0_vec = np.linspace(0.5, 10.0, n) +N12cr_vec = np.zeros((n,), dtype=dtype) +for gamma in [0.0, 0.1, 0.5, 1.0]: + for i, rho0 in enumerate(rho0_vec): + N12cr_vec[i] = con.nondimCriticalGlobalShearLoad(rho0, 0.4, gamma) + plt.plot(rho0_vec, N12cr_vec, label=f"gamma={gamma:.2f}") + +# plot it +plt.margins(x=0.05, y=0.05) +plt.xlabel(r"$\rho_0$") +plt.ylabel(r"$N_{12,cr}^*$") +plt.legend() +plt.savefig("1-verify-shear.png", dpi=400) diff --git a/examples/gp_panel_buckling/2_axialGP.py b/examples/gp_panel_buckling/2_axialGP.py new file mode 100644 index 000000000..63b9b73a8 --- /dev/null +++ b/examples/gp_panel_buckling/2_axialGP.py @@ -0,0 +1,85 @@ +""" +Check axial closed-form modes in GPBladeStiffenedShellConstitutive class are reasonable +@Author Sean Engelstad +@Date 05/16/2024 + +Use the repo https://github.com/smdogroup/ml_buckling +""" + +import numpy as np +import matplotlib.pyplot as plt +import niceplots +from tacs import TACS, constitutive +import ml_buckling as mlb + +DEG2RAD = np.pi / 180.0 + +dtype = TACS.dtype + +# Create the orthotropic layup +ortho_prop = constitutive.MaterialProperties( + rho=1550, + specific_heat=921.096, + E1=54e3, + E2=18e3, + nu12=0.25, + G12=9e3, + G13=9e3, + G23=9e3, + Xt=2410.0, + Xc=1040.0, + Yt=73.0, + Yc=173.0, + S12=71.0, + alpha=24.0e-6, + kappa=230.0, +) +ortho_ply = constitutive.OrthotropicPly(1e-3, ortho_prop) + +# build the axial GP object (which is the main ML object we are testing for this example) +# however it is used inside of the constitutive object so we need to build that too +axialGP = constitutive.BucklingGP.from_csv( + csv_file=mlb.axialGP_csv, theta_csv=mlb.axial_theta_csv +) +panelGP = constitutive.PanelGPs(axialGP=axialGP) + +# don't put in any GP models (so using closed-form solutions rn) +con = constitutive.GPBladeStiffenedShellConstitutive( + panelPly=ortho_ply, + stiffenerPly=ortho_ply, + panelLength=2.0, + stiffenerPitch=0.2, + panelThick=1.5e-2, + panelPlyAngles=np.array([0.0, 45.0, 90.0], dtype=dtype) * DEG2RAD, + panelPlyFracs=np.array([0.5, 0.3, 0.2], dtype=dtype), + stiffenerHeight=0.075, + stiffenerThick=1e-2, + stiffenerPlyAngles=np.array([0.0, 60.0], dtype=dtype) * DEG2RAD, + stiffenerPlyFracs=np.array([0.6, 0.4], dtype=dtype), + panelWidth=1.0, + flangeFraction=0.8, + panelGPs=panelGP, +) +# Set the KS weight really low so that all failure modes make a +# significant contribution to the failure function derivatives +# be careful changing the KS weight => will retrain alpha.. +# con.setKSWeight(20.0) + +xi = 1.0 # 0.4 + +# get the axial loads in nondimensional space w.r.t. rho_0 +n = 500 +plt.style.use(niceplots.get_style()) +rho0_vec = np.linspace(0.5, 10.0, n) +N11cr_vec = np.zeros((n,), dtype=TACS.dtype) +for gamma in [0.0, 0.1, 0.5, 1.0]: + for i, rho0 in enumerate(rho0_vec): + N11cr_vec[i] = con.nondimCriticalGlobalAxialLoad(rho0, xi, gamma, 0.0) + plt.plot(rho0_vec, N11cr_vec, label=f"gamma={gamma:.2f}") + +# plot it +plt.margins(x=0.05, y=0.05) +plt.xlabel(r"$\rho_0$") +plt.ylabel(r"$N_{11,cr}^*$") +plt.legend() +plt.savefig("2-verify-ML.png", dpi=400) diff --git a/examples/gp_panel_buckling/2_shearGP.py b/examples/gp_panel_buckling/2_shearGP.py new file mode 100644 index 000000000..d84d6adac --- /dev/null +++ b/examples/gp_panel_buckling/2_shearGP.py @@ -0,0 +1,82 @@ +""" +Check axial closed-form modes in GPBladeStiffenedShellConstitutive class are reasonable +@Author Sean Engelstad +@Date 05/16/2024 + +Use the repo https://github.com/smdogroup/ml_buckling +""" + +import numpy as np +import matplotlib.pyplot as plt +import niceplots +from tacs import TACS, constitutive +import ml_buckling as mlb + +DEG2RAD = np.pi / 180.0 + +dtype = TACS.dtype + +# Create the orthotropic layup +ortho_prop = constitutive.MaterialProperties( + rho=1550, + specific_heat=921.096, + E1=54e3, + E2=18e3, + nu12=0.25, + G12=9e3, + G13=9e3, + G23=9e3, + Xt=2410.0, + Xc=1040.0, + Yt=73.0, + Yc=173.0, + S12=71.0, + alpha=24.0e-6, + kappa=230.0, +) +ortho_ply = constitutive.OrthotropicPly(1e-3, ortho_prop) + +# build the axial GP object (which is the main ML object we are testing for this example) +# however it is used inside of the constitutive object so we need to build that too +shearGP = constitutive.BucklingGP.from_csv( + csv_file=mlb.shearGP_csv, theta_csv=mlb.shear_theta_csv +) +panelGP = constitutive.PanelGPs(shearGP=shearGP) + +# don't put in any GP models (so using closed-form solutions rn) +con = constitutive.GPBladeStiffenedShellConstitutive( + panelPly=ortho_ply, + stiffenerPly=ortho_ply, + panelLength=2.0, + stiffenerPitch=0.2, + panelThick=1.5e-2, + panelPlyAngles=np.array([0.0, 45.0, 90.0], dtype=dtype) * DEG2RAD, + panelPlyFracs=np.array([0.5, 0.3, 0.2], dtype=dtype), + stiffenerHeight=0.075, + stiffenerThick=1e-2, + stiffenerPlyAngles=np.array([0.0, 60.0], dtype=dtype) * DEG2RAD, + stiffenerPlyFracs=np.array([0.6, 0.4], dtype=dtype), + panelWidth=1.0, + flangeFraction=0.8, + panelGPs=panelGP, +) +# Set the KS weight really low so that all failure modes make a +# significant contribution to the failure function derivatives +con.setKSWeight(20.0) + +# get the axial loads in nondimensional space w.r.t. rho_0 +n = 500 +plt.style.use(niceplots.get_style()) +rho0_vec = np.linspace(0.5, 10.0, n) +N12cr_vec = np.zeros((n,), dtype=TACS.dtype) +for gamma in [0.0, 0.1, 0.5, 1.0]: + for i, rho0 in enumerate(rho0_vec): + N12cr_vec[i] = con.nondimCriticalGlobalShearLoad(rho0, 0.4, gamma, 0.0) + plt.plot(rho0_vec, N12cr_vec, label=f"gamma={gamma:.2f}") + +# plot it +plt.margins(x=0.05, y=0.05) +plt.xlabel(r"$\rho_0$") +plt.ylabel(r"$N_{12,cr}^*$") +plt.legend() +plt.savefig("2-verify-ML-shear.png", dpi=400) diff --git a/examples/gp_panel_buckling/3_compare_CF.py b/examples/gp_panel_buckling/3_compare_CF.py new file mode 100644 index 000000000..555b0f757 --- /dev/null +++ b/examples/gp_panel_buckling/3_compare_CF.py @@ -0,0 +1,77 @@ +""" +Check axial closed-form modes in GPBladeStiffenedShellConstitutive class are reasonable +@Author Sean Engelstad +@Date 05/16/2024 +""" + +import numpy as np +import matplotlib.pyplot as plt +import niceplots +from tacs import TACS, constitutive + +DEG2RAD = np.pi / 180.0 + +dtype = TACS.dtype + +# Create the orthotropic layup +ortho_prop = constitutive.MaterialProperties( + rho=1550, + specific_heat=921.096, + E1=54e3, + E2=18e3, + nu12=0.25, + G12=9e3, + G13=9e3, + G23=9e3, + Xt=2410.0, + Xc=1040.0, + Yt=73.0, + Yc=173.0, + S12=71.0, + alpha=24.0e-6, + kappa=230.0, +) +ortho_ply = constitutive.OrthotropicPly(1e-3, ortho_prop) + +# don't put in any GP models (so using closed-form solutions rn) +con = constitutive.GPBladeStiffenedShellConstitutive( + panelPly=ortho_ply, + stiffenerPly=ortho_ply, + panelLength=2.0, + stiffenerPitch=0.2, + panelThick=1.5e-2, + panelPlyAngles=np.array([0.0, 45.0, 90.0], dtype=dtype) * DEG2RAD, + panelPlyFracs=np.array([0.5, 0.3, 0.2], dtype=dtype), + stiffenerHeight=0.075, + stiffenerThick=1e-2, + stiffenerPlyAngles=np.array([0.0, 60.0], dtype=dtype) * DEG2RAD, + stiffenerPlyFracs=np.array([0.6, 0.4], dtype=dtype), + panelWidth=1.0, + flangeFraction=0.8, +) +# Set the KS weight really low so that all failure modes make a +# significant contribution to the failure function derivatives +con.setKSWeight(20.0) + +# get the axial loads in nondimensional space w.r.t. rho_0 +n = 500 +plt.style.use(niceplots.get_style()) +rho0_vec = np.linspace(0.31, 10.0, n) +N11cr_vec = np.zeros((n,), dtype=dtype) +N12cr_vec = np.zeros((n,), dtype=dtype) + +# rho0 = 0.3121 +xi = 0.9487 +gamma = 5.868 +for i, rho0 in enumerate(rho0_vec): + N11cr_vec[i] = con.nondimCriticalGlobalAxialLoad(rho0, xi, gamma) + N12cr_vec[i] = con.nondimCriticalGlobalShearLoad(rho0, xi, gamma) +plt.plot(rho0_vec, N11cr_vec, label="axial") +plt.plot(rho0_vec, N12cr_vec, label="shear") + +# plot it +plt.margins(x=0.05, y=0.05) +plt.xlabel(r"$\rho_0$") +plt.ylabel(r"$N_{ij,cr}^*$") +plt.legend() +plt.savefig("3-CF-compare.png", dpi=400) diff --git a/examples/gp_panel_buckling/3_compare_ML.py b/examples/gp_panel_buckling/3_compare_ML.py new file mode 100644 index 000000000..a9443b6ff --- /dev/null +++ b/examples/gp_panel_buckling/3_compare_ML.py @@ -0,0 +1,94 @@ +""" +Check axial closed-form modes in GPBladeStiffenedShellConstitutive class are reasonable +@Author Sean Engelstad +@Date 05/16/2024 + +Use the repo https://github.com/smdogroup/ml_buckling +""" + +import numpy as np +import matplotlib.pyplot as plt +import niceplots +from tacs import TACS, constitutive +import ml_buckling as mlb + +DEG2RAD = np.pi / 180.0 + +dtype = TACS.dtype + +# Create the orthotropic layup +ortho_prop = constitutive.MaterialProperties( + rho=1550, + specific_heat=921.096, + E1=54e3, + E2=18e3, + nu12=0.25, + G12=9e3, + G13=9e3, + G23=9e3, + Xt=2410.0, + Xc=1040.0, + Yt=73.0, + Yc=173.0, + S12=71.0, + alpha=24.0e-6, + kappa=230.0, +) +ortho_ply = constitutive.OrthotropicPly(1e-3, ortho_prop) + +# build the axial GP object (which is the main ML object we are testing for this example) +# however it is used inside of the constitutive object so we need to build that too +axialGP = constitutive.BucklingGP.from_csv( + csv_file=mlb.axialGP_csv, theta_csv=mlb.axial_theta_csv +) +shearGP = constitutive.BucklingGP.from_csv( + csv_file=mlb.shearGP_csv, theta_csv=mlb.shear_theta_csv +) +panelGP = constitutive.PanelGPs(axialGP=axialGP, shearGP=shearGP) + +# don't put in any GP models (so using closed-form solutions rn) +con = constitutive.GPBladeStiffenedShellConstitutive( + panelPly=ortho_ply, + stiffenerPly=ortho_ply, + panelLength=2.0, + stiffenerPitch=0.2, + panelThick=1.5e-2, + panelPlyAngles=np.array([0.0, 45.0, 90.0], dtype=dtype) * DEG2RAD, + panelPlyFracs=np.array([0.5, 0.3, 0.2], dtype=dtype), + stiffenerHeight=0.075, + stiffenerThick=1e-2, + stiffenerPlyAngles=np.array([0.0, 60.0], dtype=dtype) * DEG2RAD, + stiffenerPlyFracs=np.array([0.6, 0.4], dtype=dtype), + panelWidth=1.0, + flangeFraction=0.8, + panelGPs=panelGP, +) +# Set the KS weight really low so that all failure modes make a +# significant contribution to the failure function derivatives +con.setKSWeight(20.0) + +# get the axial loads in nondimensional space w.r.t. rho_0 +n = 500 +plt.style.use(niceplots.get_style()) +rho0_vec = np.linspace(0.2, 10.0, n) +N11cr_vec = np.zeros((n,), dtype=TACS.dtype) +N12cr_vec = np.zeros((n,), dtype=TACS.dtype) + +# rho0 = 0.3121 +xi = 0.9487 +gamma = 5.868 +zeta = 0.0035 + +for i, rho0 in enumerate(rho0_vec): + N11cr_vec[i] = con.nondimCriticalGlobalAxialLoad(rho0, xi, gamma, zeta) + N12cr_vec[i] = con.nondimCriticalGlobalShearLoad(rho0, xi, gamma, zeta) +plt.plot(rho0_vec, N11cr_vec, label="axial") +plt.plot(rho0_vec, N12cr_vec, label="shear") + + +# plot it +plt.margins(x=0.05, y=0.05) +plt.xlabel(r"$\rho_0$") +plt.ylabel(r"$N_{ij,cr}^*$") +plt.legend() +plt.savefig("3-compare-ML.png", dpi=400) diff --git a/examples/gp_panel_buckling/4_plot_ML.py b/examples/gp_panel_buckling/4_plot_ML.py new file mode 100644 index 000000000..82d7894e2 --- /dev/null +++ b/examples/gp_panel_buckling/4_plot_ML.py @@ -0,0 +1,114 @@ +""" +Check axial closed-form modes in GPBladeStiffenedShellConstitutive class are reasonable +@Author Sean Engelstad +@Date 05/16/2024 + +Use the repo https://github.com/smdogroup/ml_buckling +""" + +import numpy as np +import matplotlib.pyplot as plt +import niceplots +from tacs import TACS, constitutive +import ml_buckling as mlb + +DEG2RAD = np.pi / 180.0 + +dtype = TACS.dtype + +# Create the orthotropic layup +ortho_prop = constitutive.MaterialProperties( + rho=1550, + specific_heat=921.096, + E1=54e3, + E2=18e3, + nu12=0.25, + G12=9e3, + G13=9e3, + G23=9e3, + Xt=2410.0, + Xc=1040.0, + Yt=73.0, + Yc=173.0, + S12=71.0, + alpha=24.0e-6, + kappa=230.0, +) +ortho_ply = constitutive.OrthotropicPly(1e-3, ortho_prop) + +# build the axial GP object (which is the main ML object we are testing for this example) +# however it is used inside of the constitutive object so we need to build that too +axialGP = constitutive.BucklingGP.from_csv( + csv_file=mlb.axialGP_csv, theta_csv=mlb.axial_theta_csv +) +shearGP = constitutive.BucklingGP.from_csv( + csv_file=mlb.shearGP_csv, theta_csv=mlb.shear_theta_csv +) +panelGP = constitutive.PanelGPs(axialGP=axialGP, shearGP=shearGP) + +# don't put in any GP models (so using closed-form solutions rn) +con = constitutive.GPBladeStiffenedShellConstitutive( + panelPly=ortho_ply, + stiffenerPly=ortho_ply, + panelLength=2.0, + stiffenerPitch=0.2, + panelThick=1.5e-2, + panelPlyAngles=np.array([0.0, 45.0, 90.0], dtype=dtype) * DEG2RAD, + panelPlyFracs=np.array([0.5, 0.3, 0.2], dtype=dtype), + stiffenerHeight=0.075, + stiffenerThick=1e-2, + stiffenerPlyAngles=np.array([0.0, 60.0], dtype=dtype) * DEG2RAD, + stiffenerPlyFracs=np.array([0.6, 0.4], dtype=dtype), + panelWidth=1.0, + flangeFraction=0.8, + panelGPs=panelGP, +) +# Set the KS weight really low so that all failure modes make a +# significant contribution to the failure function derivatives +con.setKSWeight(20.0) + +# get the axial loads in nondimensional space w.r.t. rho_0 +n = 500 +plt.style.use(niceplots.get_style()) +rho0_vec = np.linspace(0.5, 10.0, n) +N11cr_vec = np.zeros((n,), dtype=TACS.dtype) +N12cr_vec = np.zeros((n,), dtype=TACS.dtype) + +# rho0 = 0.3121 +xi = 0.9487 +gamma = 5.868 +zeta = 0.0035 + +plt.figure("axial") +for igamma, gamma in enumerate([0.0, 0.5, 1.0, 3.0, 10.0]): + for i, rho0 in enumerate(rho0_vec): + N11cr_vec[i] = con.nondimCriticalGlobalAxialLoad(rho0, xi, gamma, zeta) + N12cr_vec[i] = con.nondimCriticalGlobalShearLoad(rho0, xi, gamma, zeta) + plt.plot(rho0_vec, N11cr_vec, label="axial" + str(igamma)) + # plt.plot(rho0_vec, N12cr_vec, label="shear"+str(igamma)) + + +# plot it +plt.margins(x=0.05, y=0.05) +plt.xlabel(r"$\rho_0$") +plt.ylabel(r"$N_{11,cr}^*$") +plt.legend() +plt.savefig("4-axial-ML.png", dpi=400) +plt.close("axial") + + +plt.figure("shear") +for igamma, gamma in enumerate([0.0, 0.5, 1.0, 3.0, 10.0]): + for i, rho0 in enumerate(rho0_vec): + N11cr_vec[i] = con.nondimCriticalGlobalAxialLoad(rho0, xi, gamma, zeta) + N12cr_vec[i] = con.nondimCriticalGlobalShearLoad(rho0, xi, gamma, zeta) + # plt.plot(rho0_vec, N11cr_vec, label="axial"+str(igamma)) + plt.plot(rho0_vec, N12cr_vec, label="shear" + str(igamma)) + + +# plot it +plt.margins(x=0.05, y=0.05) +plt.xlabel(r"$\rho_0$") +plt.ylabel(r"$N_{12,cr}^*$") +plt.legend() +plt.savefig("4-shear-ML.png", dpi=400) diff --git a/examples/gp_panel_buckling/check_axial_GP.py b/examples/gp_panel_buckling/check_axial_GP.py new file mode 100644 index 000000000..ba32eafeb --- /dev/null +++ b/examples/gp_panel_buckling/check_axial_GP.py @@ -0,0 +1,36 @@ +""" +Check axial closed-form modes in GPBladeStiffenedShellConstitutive class are reasonable +@Author Sean Engelstad +@Date 05/16/2024 + +Use the repo https://github.com/smdogroup/ml_buckling +""" + +import numpy as np +import matplotlib.pyplot as plt +import niceplots +from tacs import TACS, constitutive +import ml_buckling as mlb + +DEG2RAD = np.pi / 180.0 + +dtype = TACS.dtype + +# build the axial GP object (which is the main ML object we are testing for this example) +# however it is used inside of the constitutive object so we need to build that too +axialGP = constitutive.AxialGP.from_csv(csv_file=mlb.axialGP_csv) +axialGP.setKS(10) +rho0 = 0.3121 +xi = 0.9487 +gamma = 5.868 +zeta = 0.0035 + +Xtest = np.zeros((4,)) +Xtest[0] = np.log(1.0 + xi) +Xtest[1] = np.log(rho0) +Xtest[2] = np.log(1.0 + gamma) +Xtest[3] = np.log(1.0 + 1000.0 * zeta) +pred = axialGP.predict_mean_test_data(Xtest) +print(f"\n", flush=True) +print(f"Xtest = {Xtest}") +print(f"pred = {pred}") diff --git a/examples/stiffened_plate/optimize_stiffened_plate.py b/examples/stiffened_plate/optimize_stiffened_plate.py index f138df62d..bc6710838 100644 --- a/examples/stiffened_plate/optimize_stiffened_plate.py +++ b/examples/stiffened_plate/optimize_stiffened_plate.py @@ -65,6 +65,12 @@ class uses T stiffeners. # Process some command line arguments # ============================================================================== parser = argparse.ArgumentParser() +parser.add_argument( + "--useGP", + default=False, + action="store_true", + help="whether to use the Gaussian process buckling predictions or not", +) parser.add_argument( "--useStiffPitchDV", action="store_true", @@ -108,6 +114,7 @@ class uses T stiffeners. # Baseline panel sizing panelLength = length +panelWidth = width stiffenerPitch = 0.125 stiffenerPitchMin = 0.1 @@ -201,26 +208,54 @@ def element_callback(dvNum, compID, compDescript, elemDescripts, specialDVs, **k skin_ply_fraction_dv_nums = -np.ones(len(ply_angles), dtype=np.intc) stiffener_ply_fraction_dv_nums = -np.ones(len(ply_angles), dtype=np.intc) - con = constitutive.BladeStiffenedShellConstitutive( - panelPly=ply, - stiffenerPly=ply, - panelLength=panelLength, - stiffenerPitch=stiffenerPitch, - panelThick=panelThickness, - panelPlyAngles=ply_angles, - panelPlyFracs=skin_ply_fractions, - stiffenerHeight=stiffenerHeight, - stiffenerThick=stiffenerThickness, - stiffenerPlyAngles=ply_angles, - stiffenerPlyFracs=stiffener_ply_fractions, - panelLengthNum=panelLengthNum, - stiffenerPitchNum=stiffenerPitchNum, - panelThickNum=panelThicknessNum, - panelPlyFracNums=skin_ply_fraction_dv_nums, - stiffenerHeightNum=stiffenerHeightNum, - stiffenerThickNum=stiffenerThicknessNum, - stiffenerPlyFracNums=stiffener_ply_fraction_dv_nums, - ) + if not (args.useGP): + con = constitutive.BladeStiffenedShellConstitutive( + panelPly=ply, + stiffenerPly=ply, + panelLength=panelLength, + stiffenerPitch=stiffenerPitch, + panelThick=panelThickness, + panelPlyAngles=ply_angles, + panelPlyFracs=skin_ply_fractions, + stiffenerHeight=stiffenerHeight, + stiffenerThick=stiffenerThickness, + stiffenerPlyAngles=ply_angles, + stiffenerPlyFracs=stiffener_ply_fractions, + panelLengthNum=panelLengthNum, + stiffenerPitchNum=stiffenerPitchNum, + panelThickNum=panelThicknessNum, + panelPlyFracNums=skin_ply_fraction_dv_nums, + stiffenerHeightNum=stiffenerHeightNum, + stiffenerThickNum=stiffenerThicknessNum, + stiffenerPlyFracNums=stiffener_ply_fraction_dv_nums, + ) + else: # args.useGP is True + panelWidthNum = -1 + + con = constitutive.GPBladeStiffenedShellConstitutive( + panelPly=ply, + stiffenerPly=ply, + panelLength=panelLength, + stiffenerPitch=stiffenerPitch, + panelThick=panelThickness, + panelPlyAngles=ply_angles, + panelPlyFracs=skin_ply_fractions, + stiffenerHeight=stiffenerHeight, + stiffenerThick=stiffenerThickness, + stiffenerPlyAngles=ply_angles, + stiffenerPlyFracs=stiffener_ply_fractions, + panelLengthNum=panelLengthNum, + stiffenerPitchNum=stiffenerPitchNum, + panelThickNum=panelThicknessNum, + panelPlyFracNums=skin_ply_fraction_dv_nums, + stiffenerHeightNum=stiffenerHeightNum, + stiffenerThickNum=stiffenerThicknessNum, + stiffenerPlyFracNums=stiffener_ply_fraction_dv_nums, + panelWidth=panelWidth, + panelWidthNum=panelWidthNum, + flangeFraction=0.0, + ) + con.setStiffenerPitchBounds(stiffenerPitchMin, stiffenerPitchMax) con.setPanelThicknessBounds(panelThicknessMin, panelThicknessMax) con.setStiffenerThicknessBounds(stiffenerThicknessMin, stiffenerThicknessMax) diff --git a/src/TACSAssembler.cpp b/src/TACSAssembler.cpp index 59e1c847d..1be909176 100644 --- a/src/TACSAssembler.cpp +++ b/src/TACSAssembler.cpp @@ -6040,7 +6040,8 @@ void TACSAssembler::getElementOutputData(ElementType elem_type, int write_flag, /* get average output stress resultants from TACS*/ void TACSAssembler::getAverageStresses(ElementType elem_type, - TacsScalar *avgStresses) { + TacsScalar *avgStresses, + int componentNum) { // TacsScalar *avgStresses = new TacsScalar[9]; // memset(avgStresses, 0, 9); int nvals; @@ -6050,20 +6051,25 @@ void TACSAssembler::getAverageStresses(ElementType elem_type, getDataPointers(elementData, &vars, &dvars, &ddvars, NULL, &elemXpts, NULL, NULL, NULL); + int numCompElems = 0; for (int i = 0; i < numElements; i++) { - int ptr = elementNodeIndex[i]; - int len = elementNodeIndex[i + 1] - ptr; - const int *nodes = &elementTacsNodes[ptr]; - xptVec->getValues(len, nodes, elemXpts); - varsVec->getValues(len, nodes, vars); - dvarsVec->getValues(len, nodes, dvars); - ddvarsVec->getValues(len, nodes, ddvars); + // note TACS component nums are 0-based + if (elements[i]->getComponentNum() == componentNum) { + numCompElems += 1; + int ptr = elementNodeIndex[i]; + int len = elementNodeIndex[i + 1] - ptr; + const int *nodes = &elementTacsNodes[ptr]; + xptVec->getValues(len, nodes, elemXpts); + varsVec->getValues(len, nodes, vars); + dvarsVec->getValues(len, nodes, dvars); + ddvarsVec->getValues(len, nodes, ddvars); - elements[i]->getAverageStresses(i, elem_type, elemXpts, vars, dvars, ddvars, - &avgStresses[0]); + elements[i]->getAverageStresses(i, elem_type, elemXpts, vars, dvars, + ddvars, &avgStresses[0]); + } } for (int j = 0; j < 9; j++) { - avgStresses[j] /= numElements; + avgStresses[j] /= numCompElems; } -} \ No newline at end of file +} diff --git a/src/TACSAssembler.h b/src/TACSAssembler.h index b921a72ca..037018247 100644 --- a/src/TACSAssembler.h +++ b/src/TACSAssembler.h @@ -92,7 +92,8 @@ class TACSAssembler : public TACSObject { int setDependentNodes(const int *_depNodeIndex, const int *_depNodeToTacs, const double *_depNodeWeights); - void getAverageStresses(ElementType elem_type, TacsScalar *avgStresses); + void getAverageStresses(ElementType elem_type, TacsScalar *avgStresses, + int compNum); void setComplexStepGmatrix(bool flag); // Set additional information about the design vector diff --git a/src/constitutive/Makefile b/src/constitutive/Makefile index 2de69a386..e605c4daf 100644 --- a/src/constitutive/Makefile +++ b/src/constitutive/Makefile @@ -27,6 +27,9 @@ CXX_OBJS = TACSMaterialProperties.o \ TACSPhaseChangeMaterialConstitutive.o \ TACSBladeStiffenedShellConstitutive.o \ TACSSmearedCompositeShellConstitutive.o \ + TACSGaussianProcessModel.o \ + TACSPanelGPs.o \ + TACSGPBladeStiffenedShellConstitutive.o DIR=${TACS_DIR}/src/constitutive diff --git a/src/constitutive/TACSBladeStiffenedShellConstitutive.h b/src/constitutive/TACSBladeStiffenedShellConstitutive.h index 606440952..12c585e87 100644 --- a/src/constitutive/TACSBladeStiffenedShellConstitutive.h +++ b/src/constitutive/TACSBladeStiffenedShellConstitutive.h @@ -18,6 +18,7 @@ bladeFSDT model from previous versions of TACS developed by Graeme Kennedy. // ============================================================================= // Extension Includes // ============================================================================= + #include "TACSBeamConstitutive.h" #include "TACSMaterialProperties.h" #include "TACSShellConstitutive.h" @@ -885,7 +886,7 @@ class TACSBladeStiffenedShellConstitutive : public TACSShellConstitutive { * @param e Shell strains * @return TacsScalar Strength ratio */ - TacsScalar evalGlobalPanelBuckling(const TacsScalar e[]); + virtual TacsScalar evalGlobalPanelBuckling(const TacsScalar e[]); /** * @brief Compute the panel + stiffener stiffness values used to compute the @@ -907,8 +908,8 @@ class TACSBladeStiffenedShellConstitutive : public TACSShellConstitutive { * @param sens Sensitivity of the output w.r.t the shell strains * @return TacsScalar Strength Ratio */ - TacsScalar evalGlobalPanelBucklingStrainSens(const TacsScalar e[], - TacsScalar sens[]); + virtual TacsScalar evalGlobalPanelBucklingStrainSens(const TacsScalar e[], + TacsScalar sens[]); /** * @brief Add the derivative of the global panel buckling strength ratio w.r.t @@ -922,10 +923,11 @@ class TACSBladeStiffenedShellConstitutive : public TACSShellConstitutive { @param dvLen The length of the design vector (not used) @param dfdx The DV sensitivity array to add to */ - void addGlobalPanelBucklingDVSens(int elemIndex, TacsScalar scale, - const double pt[], const TacsScalar X[], - const TacsScalar strain[], int dvLen, - TacsScalar dfdx[]); + virtual void addGlobalPanelBucklingDVSens(int elemIndex, TacsScalar scale, + const double pt[], + const TacsScalar X[], + const TacsScalar strain[], + int dvLen, TacsScalar dfdx[]); /** * @brief Compute the sensitivities of the panel + stiffener stiffness values @@ -973,7 +975,7 @@ class TACSBladeStiffenedShellConstitutive : public TACSShellConstitutive { * @param e Shell strains * @return TacsScalar Strength ratio */ - TacsScalar evalLocalPanelBuckling(const TacsScalar e[]); + virtual TacsScalar evalLocalPanelBuckling(const TacsScalar e[]); /** * @brief Compute the critical axial load for local buckling of the panel @@ -1000,8 +1002,8 @@ class TACSBladeStiffenedShellConstitutive : public TACSShellConstitutive { * @param sens Sensitivity of the output w.r.t the shell strains * @return TacsScalar Strength Ratio */ - TacsScalar evalLocalPanelBucklingStrainSens(const TacsScalar e[], - TacsScalar sens[]); + virtual TacsScalar evalLocalPanelBucklingStrainSens(const TacsScalar e[], + TacsScalar sens[]); /** * @brief Add the derivative of the local panel buckling strength ratio w.r.t @@ -1015,10 +1017,11 @@ class TACSBladeStiffenedShellConstitutive : public TACSShellConstitutive { @param dvLen The length of the design vector (not used) @param dfdx The DV sensitivity array to add to */ - void addLocalPanelBucklingDVSens(int elemIndex, TacsScalar scale, - const double pt[], const TacsScalar X[], - const TacsScalar strain[], int dvLen, - TacsScalar dfdx[]); + virtual void addLocalPanelBucklingDVSens(int elemIndex, TacsScalar scale, + const double pt[], + const TacsScalar X[], + const TacsScalar strain[], int dvLen, + TacsScalar dfdx[]); /** * @brief Compute the sensitivity of the critical axial load for local @@ -1281,16 +1284,16 @@ class TACSBladeStiffenedShellConstitutive : public TACSShellConstitutive { * @param stiffenerStrain Stiffener centroid beam strains * @return TacsScalar Strength ratio */ - TacsScalar evalStiffenerCrippling(const TacsScalar stiffenerStrain[]); + virtual TacsScalar evalStiffenerCrippling(const TacsScalar stiffenerStrain[]); void computeStiffenerCripplingValues(const TacsScalar stiffenerStrain[], TacsScalar plyFailValues[]); - TacsScalar evalStiffenerCripplingStrainSens( + virtual TacsScalar evalStiffenerCripplingStrainSens( const TacsScalar stiffenerStrain[], TacsScalar sens[]); - void addStiffenerCripplingDVSens(const TacsScalar scale, - const TacsScalar stiffenerStrain[], - TacsScalar dfdx[]); + virtual void addStiffenerCripplingDVSens(const TacsScalar scale, + const TacsScalar stiffenerStrain[], + TacsScalar dfdx[]); // ============================================================================== // Attributes diff --git a/src/constitutive/TACSGPBladeStiffenedShellConstitutive.cpp b/src/constitutive/TACSGPBladeStiffenedShellConstitutive.cpp new file mode 100644 index 000000000..29ea677df --- /dev/null +++ b/src/constitutive/TACSGPBladeStiffenedShellConstitutive.cpp @@ -0,0 +1,3802 @@ +/* +===================================================================================================== +Blade-Stiffened Shell Constitutive Model using Gaussian Process Machine Learning +Buckling Constraints +===================================================================================================== +@File : TACSGPBladeStiffenedShellConstutive.cpp +@Date : 2024/04/24 +@Author : Sean Phillip Engelstad +@Description : Constitutive model for a blade-stiffened shell. Based on the FSDT +blade models adopted by Alasdair Christison Gray in the +TACSBladeStiffenedShellConstutitutive.h class and the original one by Graeme +Kennedy. Gaussian Processes for Machine Learning are used for the buckling +constraints of the stiffened panels. +*/ + +// ============================================================================= +// Standard Library Includes +// ============================================================================= + +// ============================================================================= +// Extension Includes +// ============================================================================= +#include "TACSGPBladeStiffenedShellConstitutive.h" + +#include "TACSMaterialProperties.h" +#include "TACSShellConstitutive.h" + +const char* TACSGPBladeStiffenedShellConstitutive::constName = + "TACSGPBladeStiffenedShellConstitutive"; + +// ============================================================================== +// Constructor +// ============================================================================== + +TACSGPBladeStiffenedShellConstitutive::TACSGPBladeStiffenedShellConstitutive( + TACSOrthotropicPly* panelPly, TACSOrthotropicPly* stiffenerPly, + TacsScalar kcorr, TacsScalar panelLength, int panelLengthNum, + TacsScalar stiffenerPitch, int stiffenerPitchNum, TacsScalar panelThick, + int panelThickNum, int numPanelPlies, TacsScalar panelPlyAngles[], + TacsScalar panelPlyFracs[], int panelPlyFracNums[], + TacsScalar stiffenerHeight, int stiffenerHeightNum, + TacsScalar stiffenerThick, int stiffenerThickNum, int numStiffenerPlies, + TacsScalar stiffenerPlyAngles[], TacsScalar stiffenerPlyFracs[], + int stiffenerPlyFracNums[], TacsScalar panelWidth, int panelWidthNum, + TacsScalar flangeFraction, bool CPTstiffenerCrippling, + TACSPanelGPs* panelGPs) + : TACSBladeStiffenedShellConstitutive( + panelPly, stiffenerPly, kcorr, panelLength, panelLengthNum, + stiffenerPitch, stiffenerPitchNum, panelThick, panelThickNum, + numPanelPlies, panelPlyAngles, panelPlyFracs, panelPlyFracNums, + stiffenerHeight, stiffenerHeightNum, stiffenerThick, + stiffenerThickNum, numStiffenerPlies, stiffenerPlyAngles, + stiffenerPlyFracs, stiffenerPlyFracNums, flangeFraction) { + // DVs section, only one new DV - panelWidth + // --- Panel width values --- + this->panelWidth = panelWidth; + this->panelWidthNum = panelWidthNum; + this->panelWidthLocalNum = -1; + if (panelWidthNum >= 0) { + this->panelWidthLocalNum = this->numDesignVars; + this->numDesignVars++; + this->numGeneralDV++; + } + this->panelWidthLowerBound = 0.000; + this->panelWidthUpperBound = 1e20; + + // set whether to use CPT stiffener crippling from Sean's paper vs. + // experimental DOD manuscript crippling solution from Ali's superclass + // (default false since DOD probably more accurate) + this->CPTstiffenerCrippling = CPTstiffenerCrippling; + + // set Gaussian process models in + this->panelGPs = panelGPs; + if (this->panelGPs) { + this->panelGPs->incref(); + } + + // allocate Xtest temporary vectors for each GP + if (this->getAxialGP()) { + XtestAxial = new TacsScalar[this->getAxialGP()->getNparam()]; + XtestAxialSens = new TacsScalar[this->getAxialGP()->getNparam()]; + } + if (this->getShearGP()) { + XtestShear = new TacsScalar[this->getShearGP()->getNparam()]; + XtestShearSens = new TacsScalar[this->getShearGP()->getNparam()]; + } + if (this->getCripplingGP()) { + XtestCrippling = new TacsScalar[this->getCripplingGP()->getNparam()]; + XtestCripplingSens = new TacsScalar[this->getCripplingGP()->getNparam()]; + } + + // default value of ksWeight + this->setKSWeight(100.0); +} + +// ============================================================================== +// Destructor +// ============================================================================== +TACSGPBladeStiffenedShellConstitutive:: + ~TACSGPBladeStiffenedShellConstitutive() { + // Don't call the base class destructor C++ does that automatically (and will + // seg fault if you call it here.) + + if (this->panelGPs) { + this->panelGPs->decref(); + this->panelGPs = nullptr; + } + + if (this->getAxialGP()) { + delete[] XtestAxial; + XtestAxial = nullptr; + + delete[] XtestAxialSens; + XtestAxialSens = nullptr; + } + + if (this->getShearGP()) { + delete[] XtestShear; + XtestShear = nullptr; + + delete[] XtestShearSens; + XtestShearSens = nullptr; + } + + if (this->getCripplingGP()) { + delete[] XtestCrippling; + XtestCrippling = nullptr; + + delete[] XtestCripplingSens; + XtestCripplingSens = nullptr; + } +} + +// ============================================================================== +// Override Failure constraint and sensitivities +// ============================================================================== + +TacsScalar TACSGPBladeStiffenedShellConstitutive::evalLocalPanelBuckling( + const TacsScalar e[]) { + // this routine computes N11,cr for the local panel section with size a x s_p + // (in between stiffeners) + + // compute panel stiffness matrix + TacsScalar panelStiffness[NUM_TANGENT_STIFFNESS_ENTRIES], + panelStress[NUM_STRESSES]; + this->computePanelStiffness(panelStiffness); + const TacsScalar *Ap, *Dp; + this->extractTangentStiffness(panelStiffness, &Ap, NULL, &Dp, NULL, NULL); + this->computePanelStress(e, panelStress); + + // extract panel stiffnesses and dimensions + TacsScalar D11Local = Dp[0]; + TacsScalar D12p = Dp[1], D66p = Dp[5], D22p = Dp[3]; + TacsScalar A11p = Ap[0], A66p = Ap[5]; + TacsScalar a = this->panelLength; + TacsScalar b = this->panelWidth; + TacsScalar s_p = this->stiffenerPitch; + + // compute non-dimensional parameters for the local panel + TacsScalar rho0Local = computeAffineAspectRatio( + D11Local, D22p, a, s_p); // local panel is a x s_p + TacsScalar xiLocal = computeLaminateIsotropy(D11Local, D22p, D12p, D66p); + TacsScalar zetaPanel = + computeTransverseShearParameter(A66p, A11p, b, this->panelThick); + + // compute the pure axial and pure shear buckling loads + TacsScalar N1CritLocal = computeCriticalLocalAxialLoad( + D11Local, D22p, rho0Local, xiLocal, zetaPanel); + TacsScalar N12CritLocal = computeCriticalLocalShearLoad( + D11Local, D22p, rho0Local, xiLocal, zetaPanel); + + // compute the combined loading buckling failure index + return this->bucklingEnvelope(-panelStress[0], N1CritLocal, panelStress[2], + N12CritLocal); +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::evalGlobalPanelBuckling( + const TacsScalar e[]) { + // this routine computes N11,cr for the global panel with the stiffeners + // applied + + // compute panel stiffness matrix + TacsScalar panelStiffness[NUM_TANGENT_STIFFNESS_ENTRIES], + panelStress[NUM_STRESSES]; + this->computePanelStiffness(panelStiffness); + const TacsScalar *Ap, *Dp; + this->extractTangentStiffness(panelStiffness, &Ap, NULL, &Dp, NULL, NULL); + this->computePanelStress(e, panelStress); + + // compute effective moduli, overall centroid + TacsScalar E1s, E1p, _; + this->computeEffectiveModulii(this->numPanelPlies, this->panelQMats, + this->panelPlyFracs, &E1p, &_); + this->computeEffectiveModulii(this->numStiffenerPlies, this->stiffenerQMats, + this->stiffenerPlyFracs, &E1s, &_); + TacsScalar zn = this->computeOverallCentroid(E1p, E1s); + + // get the global buckling D11 with overall stiffener + panel centroid + TacsScalar D11Global; + computePanelGlobalBucklingStiffness(E1p, zn, &D11Global); + + // extract panel stiffnesses and dimensions + TacsScalar D12p = Dp[1], D66p = Dp[5], D22p = Dp[3]; + TacsScalar A11p = Ap[0], A66p = Ap[5]; + TacsScalar a = this->panelLength; + TacsScalar b = this->panelWidth; + TacsScalar s_p = this->stiffenerPitch; + + // compute non-dimensional parameters for the global panel + TacsScalar delta = computeStiffenerAreaRatio(E1p, E1s); + TacsScalar rho0Global = + computeAffineAspectRatio(D11Global, D22p, a, b); // global panel is a x b + TacsScalar xiGlobal = computeLaminateIsotropy(D11Global, D22p, D12p, D66p); + TacsScalar gamma = computeStiffenerStiffnessRatio(D11Global, E1s, zn); + TacsScalar zetaPanel = + computeTransverseShearParameter(A66p, A11p, b, this->panelThick); + + // compute the pure axial and pure shear buckling loads + TacsScalar N1CritGlobal = computeCriticalGlobalAxialLoad( + D11Global, D22p, b, delta, rho0Global, xiGlobal, gamma, zetaPanel); + TacsScalar N12CritGlobal = computeCriticalGlobalShearLoad( + D11Global, D22p, b, rho0Global, xiGlobal, gamma, zetaPanel); + + // compute the combined loading buckling failure index + return this->bucklingEnvelope(-panelStress[0], N1CritGlobal, panelStress[2], + N12CritGlobal); +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::evalStiffenerCrippling( + const TacsScalar stiffenerStrain[]) { + if (CPTstiffenerCrippling) { // use predictions for Sean's paper + // compute D matrix of the stiffener (treating it like a panel for + // crippling) + TacsScalar stiffenerCripplingStiffness[NUM_TANGENT_STIFFNESS_ENTRIES]; + const TacsScalar *As_crippling, *Ds_crippling; + this->computeStiffenerCripplingStiffness(stiffenerCripplingStiffness); + this->extractTangentStiffness(stiffenerCripplingStiffness, &As_crippling, + NULL, &Ds_crippling, NULL, NULL); + + // get stiffener material props and dimensions + TacsScalar A11s = As_crippling[0], A66s = As_crippling[5]; + TacsScalar D11s = Ds_crippling[0], D12s = Ds_crippling[1]; + TacsScalar D22s = Ds_crippling[3], D66s = Ds_crippling[5]; + TacsScalar bStiff = this->stiffenerHeight; + TacsScalar hStiff = this->stiffenerThick; + TacsScalar a = this->panelLength; + + // compute stiffener non-dimensional parameters + TacsScalar rho0Stiff = computeAffineAspectRatio(D11s, D22s, a, bStiff); + TacsScalar xiStiff = computeLaminateIsotropy(D11s, D22s, D12s, D66s); + TacsScalar genPoiss = computeGeneralizedPoissonsRatio(D12s, D66s); + TacsScalar zetaStiff = + computeTransverseShearParameter(A66s, A11s, bStiff, hStiff); + + // Compute stiffener in plane load and crippling failure index + TacsScalar A11s_beam; + TacsScalar N1stiff = + computeStiffenerInPlaneLoad(stiffenerStrain, &A11s_beam); + TacsScalar N1CritCrippling = computeStiffenerCripplingLoad( + D11s, D22s, xiStiff, rho0Stiff, genPoiss, zetaStiff); + + // return the stiffener failure index + return N1stiff / N1CritCrippling; + + } else { // CPTstiffenerCrippling = false + // the DOD experimental stiffener crippling solution from Ali's superclass + return TACSBladeStiffenedShellConstitutive::evalStiffenerCrippling( + stiffenerStrain); + } +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::evalLocalPanelBucklingStrainSens( + const TacsScalar e[], TacsScalar sens[]) { + // this routine computes N11,cr for the local panel section with size a x s_p + // (in between stiffeners) + + // compute panel stiffness matrix + TacsScalar panelStiffness[NUM_TANGENT_STIFFNESS_ENTRIES], + panelStress[NUM_STRESSES]; + this->computePanelStiffness(panelStiffness); + const TacsScalar *Ap, *Dp; + this->extractTangentStiffness(panelStiffness, &Ap, NULL, &Dp, NULL, NULL); + this->computePanelStress(e, panelStress); + + // extract panel stiffnesses and dimensions + TacsScalar D11Local = Dp[0]; + TacsScalar D12p = Dp[1], D66p = Dp[5], D22p = Dp[3]; + TacsScalar A11p = Ap[0], A66p = Ap[5]; + TacsScalar a = this->panelLength; + TacsScalar b = this->panelWidth; + TacsScalar s_p = this->stiffenerPitch; + + // compute non-dimensional parameters for the local panel + TacsScalar rho0Local = computeAffineAspectRatio( + D11Local, D22p, a, s_p); // local panel is a x s_p + TacsScalar xiLocal = computeLaminateIsotropy(D11Local, D22p, D12p, D66p); + TacsScalar zetaPanel = + computeTransverseShearParameter(A66p, A11p, b, this->panelThick); + + // compute the pure axial and pure shear buckling loads + TacsScalar N1CritLocal = computeCriticalLocalAxialLoad( + D11Local, D22p, rho0Local, xiLocal, zetaPanel); + TacsScalar N12CritLocal = computeCriticalLocalShearLoad( + D11Local, D22p, rho0Local, xiLocal, zetaPanel); + + // Compute the buckling criteria and it's sensitivities + TacsScalar N1LocalSens, N12LocalSens, N1CritLocalSens, N12CritLocalSens; + const TacsScalar strengthRatio = this->bucklingEnvelopeSens( + -panelStress[0], N1CritLocal, panelStress[2], N12CritLocal, &N1LocalSens, + &N1CritLocalSens, &N12LocalSens, &N12CritLocalSens); + + sens[0] = N1LocalSens * -Ap[0] + N12LocalSens * Ap[2]; + sens[1] = N1LocalSens * -Ap[1] + N12LocalSens * Ap[4]; + sens[2] = N1LocalSens * -Ap[2] + N12LocalSens * Ap[5]; + for (int ii = 3; ii < NUM_STRESSES; ii++) { + sens[ii] = 0.0; + } + + return strengthRatio; +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::evalGlobalPanelBucklingStrainSens( + const TacsScalar e[], TacsScalar sens[]) { + // this routine computes N11,cr for the global panel with the stiffeners + // applied + + // compute panel stiffness matrix + TacsScalar panelStiffness[NUM_TANGENT_STIFFNESS_ENTRIES], + panelStress[NUM_STRESSES]; + this->computePanelStiffness(panelStiffness); + const TacsScalar *Ap, *Dp; + this->extractTangentStiffness(panelStiffness, &Ap, NULL, &Dp, NULL, NULL); + this->computePanelStress(e, panelStress); + + // compute effective moduli, overall centroid + TacsScalar E1s, E1p, _; + this->computeEffectiveModulii(this->numPanelPlies, this->panelQMats, + this->panelPlyFracs, &E1p, &_); + this->computeEffectiveModulii(this->numStiffenerPlies, this->stiffenerQMats, + this->stiffenerPlyFracs, &E1s, &_); + TacsScalar zn = this->computeOverallCentroid(E1p, E1s); + + // get the global buckling D11 with overall stiffener + panel centroid + TacsScalar D11Global; + computePanelGlobalBucklingStiffness(E1p, zn, &D11Global); + + // extract panel stiffnesses and dimensions + TacsScalar D12p = Dp[1], D66p = Dp[5], D22p = Dp[3]; + TacsScalar A11p = Ap[0], A66p = Ap[5]; + TacsScalar a = this->panelLength; + TacsScalar b = this->panelWidth; + TacsScalar s_p = this->stiffenerPitch; + + // compute non-dimensional parameters for the global panel + TacsScalar delta = computeStiffenerAreaRatio(E1p, E1s); + TacsScalar rho0Global = + computeAffineAspectRatio(D11Global, D22p, a, b); // global panel is a x b + TacsScalar xiGlobal = computeLaminateIsotropy(D11Global, D22p, D12p, D66p); + TacsScalar gamma = computeStiffenerStiffnessRatio(D11Global, E1s, zn); + TacsScalar zetaPanel = + computeTransverseShearParameter(A66p, A11p, b, this->panelThick); + + // compute the pure axial and pure shear buckling loads + TacsScalar N1CritGlobal = computeCriticalGlobalAxialLoad( + D11Global, D22p, b, delta, rho0Global, xiGlobal, gamma, zetaPanel); + TacsScalar N12CritGlobal = computeCriticalGlobalShearLoad( + D11Global, D22p, b, rho0Global, xiGlobal, gamma, zetaPanel); + + // backprop sensitivities from combined loading to the in-plane loads for + // strain sens + TacsScalar N1GlobalSens, N1CritGlobalSens, N12GlobalSens, N12CritGlobalSens; + const TacsScalar strengthRatio = this->bucklingEnvelopeSens( + -panelStress[0], N1CritGlobal, panelStress[2], N12CritGlobal, + &N1GlobalSens, &N1CritGlobalSens, &N12GlobalSens, &N12CritGlobalSens); + + sens[0] += N1GlobalSens * -Ap[0] + N12GlobalSens * Ap[2]; + sens[1] += N1GlobalSens * -Ap[1] + N12GlobalSens * Ap[4]; + sens[2] += N1GlobalSens * -Ap[2] + N12GlobalSens * Ap[5]; + for (int ii = 3; ii < NUM_STRESSES; ii++) { + sens[ii] = 0.0; + } + + // compute the combined loading buckling failure index + return strengthRatio; +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::evalStiffenerCripplingStrainSens( + const TacsScalar stiffenerStrain[], TacsScalar sens[]) { + if (CPTstiffenerCrippling) { // use predictions for Sean's paper + // compute D matrix of the stiffener (treating it like a panel for + // crippling) + TacsScalar stiffenerCripplingStiffness[NUM_TANGENT_STIFFNESS_ENTRIES]; + const TacsScalar *As_crippling, *Ds_crippling; + this->computeStiffenerCripplingStiffness(stiffenerCripplingStiffness); + this->extractTangentStiffness(stiffenerCripplingStiffness, &As_crippling, + NULL, &Ds_crippling, NULL, NULL); + + // get stiffener material props and dimensions + TacsScalar A11s = As_crippling[0], A66s = As_crippling[5]; + TacsScalar D11s = Ds_crippling[0], D12s = Ds_crippling[1]; + TacsScalar D22s = Ds_crippling[3], D66s = Ds_crippling[5]; + TacsScalar bStiff = this->stiffenerHeight; + TacsScalar hStiff = this->stiffenerThick; + TacsScalar a = this->panelLength; + + // compute stiffener non-dimensional parameters + TacsScalar rho0Stiff = computeAffineAspectRatio(D11s, D22s, a, bStiff); + TacsScalar xiStiff = computeLaminateIsotropy(D11s, D22s, D12s, D66s); + TacsScalar genPoiss = computeGeneralizedPoissonsRatio(D12s, D66s); + TacsScalar zetaStiff = + computeTransverseShearParameter(A66s, A11s, bStiff, hStiff); + + // Compute stiffener in plane load and crippling failure index + TacsScalar A11s_beam; + TacsScalar N1stiff = + computeStiffenerInPlaneLoad(stiffenerStrain, &A11s_beam); + TacsScalar N1CritCrippling = computeStiffenerCripplingLoad( + D11s, D22s, xiStiff, rho0Stiff, genPoiss, zetaStiff); + + // applky stiffener strain sens through the in-plane load + // Sensitivity from stiffener crippling (ignores dKSdf[4] since that is + // applied in main evalFailureStrainSens here) + TacsScalar N1stiffSens = 1.0 / N1CritCrippling; + sens[0] += N1stiffSens * -A11s_beam; + TacsScalar z = -1.0 * this->computeStiffenerCentroidHeight() - + 0.5 * this->panelThick; // stiffener downwards into wing + sens[3] += N1stiffSens * z * -A11s_beam; + + // return the stiffener failure index + return N1stiff / N1CritCrippling; + + } else { // CPTstiffenerCrippling = false + // the DOD experimental stiffener crippling solution from Ali's superclass + return TACSBladeStiffenedShellConstitutive:: + evalStiffenerCripplingStrainSens(stiffenerStrain, sens); + } +} + +void TACSGPBladeStiffenedShellConstitutive::addLocalPanelBucklingDVSens( + int elemIndex, TacsScalar scale, const double pt[], const TacsScalar X[], + const TacsScalar strain[], int dvLen, TacsScalar dfdx[]) { + // scale is scale * dKSdf[2] here so already backpropped to the combined + // axial-shear buckling load now we need to backprop through the failure + // computation back to the DVs + + // compute panel stiffness matrix + TacsScalar panelStiffness[NUM_TANGENT_STIFFNESS_ENTRIES], + panelStress[NUM_STRESSES]; + this->computePanelStiffness(panelStiffness); + const TacsScalar *Ap, *Dp; + this->extractTangentStiffness(panelStiffness, &Ap, NULL, &Dp, NULL, NULL); + this->computePanelStress(strain, panelStress); + + // extract panel stiffnesses and dimensions + TacsScalar D11Local = Dp[0]; + TacsScalar D12p = Dp[1], D66p = Dp[5], D22p = Dp[3]; + TacsScalar A11p = Ap[0], A66p = Ap[5]; + TacsScalar a = this->panelLength; + TacsScalar b = this->panelWidth; + TacsScalar s_p = this->stiffenerPitch; + + // compute non-dimensional parameters for the local panel + TacsScalar rho0Local = computeAffineAspectRatio( + D11Local, D22p, a, s_p); // local panel is a x s_p + TacsScalar xiLocal = computeLaminateIsotropy(D11Local, D22p, D12p, D66p); + TacsScalar zetaPanel = + computeTransverseShearParameter(A66p, A11p, b, this->panelThick); + + // compute the pure axial and pure shear buckling loads + TacsScalar N1CritLocal = computeCriticalLocalAxialLoad( + D11Local, D22p, rho0Local, xiLocal, zetaPanel); + TacsScalar N12CritLocal = computeCriticalLocalShearLoad( + D11Local, D22p, rho0Local, xiLocal, zetaPanel); + + // backprop from combined failure index to the in-plane load sens, critical + // buckling load sens + TacsScalar N1LocalSens, N12LocalSens, N1CritLocalSens, N12CritLocalSens; + const TacsScalar strengthRatio = this->bucklingEnvelopeSens( + -panelStress[0], N1CritLocal, panelStress[2], N12CritLocal, &N1LocalSens, + &N1CritLocalSens, &N12LocalSens, &N12CritLocalSens); + + // backprop in-plane load sens (stress sens) to the DVs + TacsScalar dfdPanelStress[NUM_STRESSES]; + memset(dfdPanelStress, 0, NUM_STRESSES * sizeof(TacsScalar)); + dfdPanelStress[0] = -N1LocalSens; + dfdPanelStress[2] = N12LocalSens; + // figure out whether this should be based on + this->addPanelStressDVSens(scale, strain, dfdPanelStress, + &dfdx[this->panelDVStartNum]); + + // backprop critical buckling load sens to the material properties and the + // non-dimensional parameters + // -------------------------- + + // holder for local DV jacobians (we then multiply them by scale when we add + // them into the dfdx) DVsens format [0 - panel length, 1 - stiff pitch, 2 - + // panel thick, + // 3 - stiff height, 4 - stiff thick, 5 - panel width] + TacsScalar DVsens[6]; + memset(DVsens, 0, 6 * sizeof(TacsScalar)); + + // define the material sensivitiies + TacsScalar Dpsens[4]; // D11Local,D12,D22,D66 + TacsScalar Apsens[4]; // A11,A12,A22,A66 + memset(Dpsens, 0, 4 * sizeof(TacsScalar)); + memset(Apsens, 0, 4 * sizeof(TacsScalar)); + + // define the non-dimensional (ND) sensitivities [rho0Local, xiLocal, zeta] + TacsScalar NDsens[3]; + memset(NDsens, 0, 3 * sizeof(TacsScalar)); + + // backprop from the critical axial + shear local buckling loads to the + // material, ND params, DVs + computeCriticalLocalAxialLoadSens( + N1CritLocalSens, D11Local, D22p, rho0Local, xiLocal, zetaPanel, + &Dpsens[0], &Dpsens[2], &DVsens[1], &NDsens[0], &NDsens[1], &NDsens[2]); + computeCriticalLocalShearLoadSens( + N12CritLocalSens, D11Local, D22p, rho0Local, xiLocal, zetaPanel, + &Dpsens[0], &Dpsens[2], &DVsens[1], &NDsens[0], &NDsens[1], &NDsens[2]); + + // backprop from ND params to the material and DVs + // ------------------- + + // note we use s_p and spitchSens in place of b and bsens for local panel + computeAffineAspectRatioSens(NDsens[0], D11Local, D22p, a, s_p, &Dpsens[0], + &Dpsens[2], &DVsens[0], + &DVsens[1]); // 0 - backprop from rho0Local + + computeLaminateIsotropySens(NDsens[1], D11Local, D22p, D12p, D66p, &Dpsens[0], + &Dpsens[2], &Dpsens[1], + &Dpsens[3]); // 1 - backprop from xiLocal + + // backprop from material sensitivities to DVs + // -------------------- + + // panel thickness term for material part.. + TacsScalar t = this->panelThick; + if (this->panelThickNum >= 0) { + int dvNum = this->panelThickLocalNum; + // backpropagate through the D matrix + TacsScalar dDfactor_dthick = 0.25 * t * t; // d/dt(t^3/12) = t^2/4 + for (int ii = 0; ii < this->numPanelPlies; ii++) { + TacsScalar* Q = &(this->panelQMats[ii * NUM_Q_ENTRIES]); + dfdx[dvNum] += scale * dDfactor_dthick * this->panelPlyFracs[ii] * + (Dpsens[0] * Q[0] + Dpsens[2] * Q[3] + Dpsens[1] * Q[1] + + Dpsens[3] * Q[5]); + dfdx[dvNum] += scale * this->panelPlyFracs[ii] * + (Apsens[0] * Q[0] + Apsens[2] * Q[3] + Apsens[1] * Q[1] + + Apsens[3] * Q[5]); + } + } + + // --- Panel Ply fraction sensitivities for A,D matrices --- + for (int plyNum = 0; plyNum < this->numPanelPlies; plyNum++) { + int dvNum = this->panelPlyFracLocalNums[plyNum]; + if (dvNum >= 0) { + const TacsScalar* Q = &(this->panelQMats[plyNum * NUM_Q_ENTRIES]); + dfdx[dvNum] += scale * (t * t * t / 12.0) * + (Dpsens[0] * Q[0] + Dpsens[2] * Q[3] + Dpsens[1] * Q[1] + + Dpsens[3] * Q[5]); + dfdx[dvNum] += scale * t * + (Apsens[0] * Q[0] + Apsens[2] * Q[3] + Apsens[1] * Q[1] + + Apsens[3] * Q[5]); + } + } + + // combine local DV jacobians with scale to write into dfdx full derivatives + // -------------------------------- + + if (this->panelLengthLocalNum >= 0) { + dfdx[this->panelLengthLocalNum] += scale * DVsens[0]; + } + if (this->stiffenerPitchLocalNum >= 0) { + dfdx[this->stiffenerPitchLocalNum] += scale * DVsens[1]; + } + if (this->panelThickLocalNum >= 0) { + dfdx[this->panelThickLocalNum] += scale * DVsens[2]; + } + if (this->stiffenerHeightLocalNum >= 0) { + dfdx[this->stiffenerHeightLocalNum] += scale * DVsens[3]; + } + if (this->stiffenerThickLocalNum >= 0) { + dfdx[this->stiffenerThickLocalNum] += scale * DVsens[4]; + } + if (this->panelWidthLocalNum >= 0) { + dfdx[this->panelWidthLocalNum] += scale * DVsens[5]; + } +} + +void TACSGPBladeStiffenedShellConstitutive::addGlobalPanelBucklingDVSens( + int elemIndex, TacsScalar scale, const double pt[], const TacsScalar X[], + const TacsScalar strain[], int dvLen, TacsScalar dfdx[]) { + // scale is scale * dKSdf[3] here so already backpropped to the combined + // axial-shear buckling load now we need to backprop through the failure + // computation back to the DVs + + // compute panel stiffness matrix + TacsScalar panelStiffness[NUM_TANGENT_STIFFNESS_ENTRIES], + panelStress[NUM_STRESSES]; + this->computePanelStiffness(panelStiffness); + const TacsScalar *Ap, *Dp; + this->extractTangentStiffness(panelStiffness, &Ap, NULL, &Dp, NULL, NULL); + this->computePanelStress(strain, panelStress); + + // compute effective moduli, overall centroid + TacsScalar E1s, E1p, _; + this->computeEffectiveModulii(this->numPanelPlies, this->panelQMats, + this->panelPlyFracs, &E1p, &_); + this->computeEffectiveModulii(this->numStiffenerPlies, this->stiffenerQMats, + this->stiffenerPlyFracs, &E1s, &_); + TacsScalar zn = this->computeOverallCentroid(E1p, E1s); + + // get the global buckling D11 with overall stiffener + panel centroid + TacsScalar D11Global; + computePanelGlobalBucklingStiffness(E1p, zn, &D11Global); + + // extract panel stiffnesses and dimensions + TacsScalar D12p = Dp[1], D66p = Dp[5], D22p = Dp[3]; + TacsScalar A11p = Ap[0], A66p = Ap[5]; + TacsScalar a = this->panelLength; + TacsScalar b = this->panelWidth; + TacsScalar s_p = this->stiffenerPitch; + TacsScalar h = this->panelThick; + + // compute non-dimensional parameters for the global panel + TacsScalar delta = computeStiffenerAreaRatio(E1p, E1s); + TacsScalar rho0Global = + computeAffineAspectRatio(D11Global, D22p, a, b); // global panel is a x b + TacsScalar xiGlobal = computeLaminateIsotropy(D11Global, D22p, D12p, D66p); + TacsScalar gamma = computeStiffenerStiffnessRatio(D11Global, E1s, zn); + TacsScalar zetaPanel = + computeTransverseShearParameter(A66p, A11p, b, this->panelThick); + + // compute the pure axial and pure shear buckling loads + TacsScalar N1CritGlobal = computeCriticalGlobalAxialLoad( + D11Global, D22p, b, delta, rho0Global, xiGlobal, gamma, zetaPanel); + TacsScalar N12CritGlobal = computeCriticalGlobalShearLoad( + D11Global, D22p, b, rho0Global, xiGlobal, gamma, zetaPanel); + + // backprop sensitivities from combined loading to the in-plane loads for + // strain sens + TacsScalar N1GlobalSens, N1CritGlobalSens, N12GlobalSens, N12CritGlobalSens; + const TacsScalar strengthRatio = this->bucklingEnvelopeSens( + -panelStress[0], N1CritGlobal, panelStress[2], N12CritGlobal, + &N1GlobalSens, &N1CritGlobalSens, &N12GlobalSens, &N12CritGlobalSens); + + // backprop in-plane load sens (stress sens) to the DVs + TacsScalar dfdPanelStress[NUM_STRESSES]; + memset(dfdPanelStress, 0, NUM_STRESSES * sizeof(TacsScalar)); + dfdPanelStress[0] = -N1GlobalSens; + dfdPanelStress[2] = N12GlobalSens; + // figure out whether this should be based on + this->addPanelStressDVSens(scale, strain, dfdPanelStress, + &dfdx[this->panelDVStartNum]); + + // backprop critical buckling load sens to the material properties and the + // non-dimensional parameters + // -------------------------- + + // holder for local DV jacobians (we then multiply them by scale when we add + // them into the dfdx) DVsens format [0 - panel length, 1 - stiff pitch, 2 - + // panel thick, + // 3 - stiff height, 4 - stiff thick, 5 - panel width] + TacsScalar DVsens[6]; + memset(DVsens, 0, 6 * sizeof(TacsScalar)); + + // sensitivities for E1p, E1s, zn which we'll handle later + TacsScalar E1p_bar = 0.0, E1s_bar = 0.0, zn_bar = 0.0; + + // define the material sensivitiies + TacsScalar Dpsens[4]; // D11Global,D12,D22,D66 + TacsScalar Apsens[4]; // A11,A12,A22,A66 + memset(Dpsens, 0, 4 * sizeof(TacsScalar)); + memset(Apsens, 0, 4 * sizeof(TacsScalar)); + + // define the non-dimensional (ND) sensitivities [rho0Global, xiGlobal, delta, + // gamma, zeta] + TacsScalar NDsens[5]; + memset(NDsens, 0, 5 * sizeof(TacsScalar)); + + // backprop from the critical axial + shear local buckling loads to the + // material, ND params, DVs + computeCriticalGlobalAxialLoadSens( + N1CritGlobalSens, D11Global, D22p, b, delta, rho0Global, xiGlobal, gamma, + zetaPanel, &Dpsens[0], &Dpsens[2], &DVsens[5], &NDsens[2], &NDsens[0], + &NDsens[1], &NDsens[3], &NDsens[4]); + computeCriticalGlobalShearLoadSens( + N12CritGlobalSens, D11Global, D22p, b, rho0Global, xiGlobal, gamma, + zetaPanel, &Dpsens[0], &Dpsens[2], &DVsens[5], &NDsens[0], &NDsens[1], + &NDsens[3], &NDsens[4]); + + // backprop from ND params to the material and DVs + // ------------------- + + computeAffineAspectRatioSens(NDsens[0], D11Global, D22p, a, b, &Dpsens[0], + &Dpsens[2], &DVsens[0], + &DVsens[5]); // 0 - backprop from rho0Global + computeLaminateIsotropySens(NDsens[1], D11Global, D22p, D12p, D66p, + &Dpsens[0], &Dpsens[2], &Dpsens[1], + &Dpsens[3]); // 1 - backprop from xiGlobal + computeStiffenerAreaRatioSens(NDsens[2], E1p, E1s, &DVsens[4], &DVsens[3], + &DVsens[1], &DVsens[2], &E1p_bar, + &E1s_bar); // 2 - backprop from delta + computeStiffenerStiffnessRatioSens( + NDsens[3], D11Global, E1s, zn, &Dpsens[0], &DVsens[4], &DVsens[3], + &DVsens[1], &E1s_bar, &zn_bar); // 3 - backprop from gamma + computeTransverseShearParameterSens( + NDsens[4], A66p, A11p, b, h, &Apsens[3], &Apsens[0], &DVsens[5], + &DVsens[2]); // 4 - backprop from zetaPanel + + // backprop global buckling sensitivities + // --------------------------------- + // backprop D11p global panel buckling to E1p, E1s and DV sensitivities + computePanelGlobalBucklingStiffnessSens(Dpsens[0], E1p, zn, &DVsens[1], + &DVsens[2], &E1p_bar, &zn_bar); + + // backpropagate zn_bar (sens) to E1p, E1s sensitivities + computeOverallCentroidSens(zn_bar, E1p, E1s, &DVsens[4], &DVsens[3], + &DVsens[2], &DVsens[1], &E1p_bar, &E1s_bar); + + // now backprop to panel ply fraction sensitivities through E1p_bar + for (int plyNum = 0; plyNum < this->numPanelPlies; plyNum++) { + int dvNum = this->panelPlyFracLocalNums[plyNum]; + if (dvNum >= 0) { + const TacsScalar* Q = &(this->panelQMats[plyNum * NUM_Q_ENTRIES]); + TacsScalar jac = (Q[0] - Q[1] * Q[1] / Q[3]); // dE1p / dply_frac[i] + dfdx[dvNum] += scale * E1p_bar * jac; + } + } + + // now backprop to stiffener ply fraction sensitivities through E1s_bar + for (int plyNum = 0; plyNum < this->numStiffenerPlies; plyNum++) { + int dvNum = this->stiffenerPlyFracLocalNums[plyNum]; + if (dvNum >= 0) { + const TacsScalar* Q = &(this->stiffenerQMats[plyNum * NUM_Q_ENTRIES]); + TacsScalar jac = (Q[0] - Q[1] * Q[1] / Q[3]); // dE1p / dply_frac[i] + dfdx[dvNum] += scale * E1s_bar * jac; + } + } + + // backprop from material sensitivities to DVs + // -------------------- + + // panel thickness term for material part.. + TacsScalar t = this->panelThick; + if (this->panelThickNum >= 0) { + int dvNum = this->panelThickLocalNum; + // backpropagate through the D matrix + TacsScalar dDfactor_dthick = 0.25 * t * t; // d/dt(t^3/12) = t^2/4 + for (int ii = 0; ii < this->numPanelPlies; ii++) { + TacsScalar* Q = &(this->panelQMats[ii * NUM_Q_ENTRIES]); + dfdx[dvNum] += scale * dDfactor_dthick * this->panelPlyFracs[ii] * + (Dpsens[2] * Q[3] + Dpsens[1] * Q[1] + + Dpsens[3] * Q[5]); // no Dpsens[0] here as global D11 + dfdx[dvNum] += scale * this->panelPlyFracs[ii] * + (Apsens[0] * Q[0] + Apsens[2] * Q[3] + Apsens[1] * Q[1] + + Apsens[3] * Q[5]); + } + } + + // --- Panel Ply fraction sensitivities for A,D matrices --- + for (int plyNum = 0; plyNum < this->numPanelPlies; plyNum++) { + int dvNum = this->panelPlyFracLocalNums[plyNum]; + if (dvNum >= 0) { + const TacsScalar* Q = &(this->panelQMats[plyNum * NUM_Q_ENTRIES]); + dfdx[dvNum] += scale * (t * t * t / 12.0) * + (Dpsens[2] * Q[3] + Dpsens[1] * Q[1] + + Dpsens[3] * Q[5]); // no Dpsens[0] here as global D11 + dfdx[dvNum] += scale * t * + (Apsens[0] * Q[0] + Apsens[2] * Q[3] + Apsens[1] * Q[1] + + Apsens[3] * Q[5]); + } + } + + // combine local DV jacobians with scale to write into dfdx full derivatives + // -------------------------------- + + if (this->panelLengthLocalNum >= 0) { + dfdx[this->panelLengthLocalNum] += scale * DVsens[0]; + } + if (this->stiffenerPitchLocalNum >= 0) { + dfdx[this->stiffenerPitchLocalNum] += scale * DVsens[1]; + } + if (this->panelThickLocalNum >= 0) { + dfdx[this->panelThickLocalNum] += scale * DVsens[2]; + } + if (this->stiffenerHeightLocalNum >= 0) { + dfdx[this->stiffenerHeightLocalNum] += scale * DVsens[3]; + } + if (this->stiffenerThickLocalNum >= 0) { + dfdx[this->stiffenerThickLocalNum] += scale * DVsens[4]; + } + if (this->panelWidthLocalNum >= 0) { + dfdx[this->panelWidthLocalNum] += scale * DVsens[5]; + } +} + +void TACSGPBladeStiffenedShellConstitutive::addStiffenerCripplingDVSens( + const TacsScalar scale, const TacsScalar stiffenerStrain[], + TacsScalar dfdx[]) { + if (CPTstiffenerCrippling) { // use predictions for Sean's paper + + // previous section writes directly into dfdx, this section writes into + // DVsens DVsens format [0 - panel length, 1 - stiff pitch, 2 - panel thick, + // 3 - stiff height, 4 - stiff thick, 5 - panel width] + TacsScalar DVsens[6]; + memset(DVsens, 0, 6 * sizeof(TacsScalar)); + + // setup new ND parameter array for stiffener computation + // ND parameter sens [xi, rho0, genPoiss, zeta] + TacsScalar stiffNDsens[4]; + memset(stiffNDsens, 0, 4 * sizeof(TacsScalar)); + + // set initial A,D matrix, nondim parameter and DV sensitivities to + // backpropagate to. + TacsScalar Ds_sens[4]; // D11,D12,D22,D66 + TacsScalar As_sens[4]; // A11,A12,A22,A66 + memset(Ds_sens, 0, 4 * sizeof(TacsScalar)); + memset(As_sens, 0, 4 * sizeof(TacsScalar)); + + // compute D matrix of the stiffener (treating it like a panel for + // crippling) + TacsScalar stiffenerCripplingStiffness[NUM_TANGENT_STIFFNESS_ENTRIES]; + const TacsScalar *As_crippling, *Ds_crippling; + this->computeStiffenerCripplingStiffness(stiffenerCripplingStiffness); + this->extractTangentStiffness(stiffenerCripplingStiffness, &As_crippling, + NULL, &Ds_crippling, NULL, NULL); + + // temporarily modify + TacsScalar A11s = As_crippling[0], A66s = As_crippling[5]; + TacsScalar D11s = Ds_crippling[0], D12s = Ds_crippling[1]; + TacsScalar D22s = Ds_crippling[3], D66s = Ds_crippling[5]; + TacsScalar bStiff = this->stiffenerHeight; + TacsScalar hStiff = this->stiffenerThick; + TacsScalar a = this->panelLength; + + // compute stiffener non-dimensional parameters + TacsScalar rho0Stiff = computeAffineAspectRatio(D11s, D22s, a, bStiff); + TacsScalar xiStiff = computeLaminateIsotropy(D11s, D22s, D12s, D66s); + TacsScalar genPoiss = computeGeneralizedPoissonsRatio(D12s, D66s); + TacsScalar zetaStiff = + computeTransverseShearParameter(A66s, A11s, bStiff, hStiff); + + // Compute stiffener in plane load and crippling failure index + TacsScalar A11s_beam; + TacsScalar N1stiff = + computeStiffenerInPlaneLoad(stiffenerStrain, &A11s_beam); + // TacsScalar N1stiff = 1.0; + TacsScalar N1CritCrippling = computeStiffenerCripplingLoad( + D11s, D22s, xiStiff, rho0Stiff, genPoiss, zetaStiff); + TacsScalar fail_index = N1stiff / N1CritCrippling; + // --- End of computeFailuresValues subsections --- + + // backpropagate from fail index to N1 stiffener in plane load + TacsScalar stiffN1sens = 1.0 / N1CritCrippling; + this->computeStiffenerInPlaneLoadSens(scale, stiffenerStrain, stiffN1sens, + &dfdx[this->stiffenerDVStartNum]); + + // backpropagate N1crit sens of stiffener to ND and material sensitivities + TacsScalar stiffN1critsens = fail_index * -1.0 / N1CritCrippling; + computeStiffenerCripplingLoadSens( + stiffN1critsens, D11s, D22s, xiStiff, rho0Stiff, genPoiss, zetaStiff, + &Ds_sens[0], &Ds_sens[2], &DVsens[3], &stiffNDsens[0], &stiffNDsens[1], + &stiffNDsens[2], &stiffNDsens[3]); + + // backpropagate ND sensitivities to A, D matrix for stiffener and DV sens + computeLaminateIsotropySens(stiffNDsens[0], D11s, D22s, D12s, D66s, + &Ds_sens[0], &Ds_sens[2], &Ds_sens[1], + &Ds_sens[3]); + computeAffineAspectRatioSens(stiffNDsens[1], D11s, D22s, a, bStiff, + &Ds_sens[0], &Ds_sens[2], &DVsens[0], + &DVsens[3]); + computeGeneralizedPoissonsRatioSens(stiffNDsens[2], D12s, D66s, &Ds_sens[1], + &Ds_sens[3]); + computeTransverseShearParameterSens(stiffNDsens[3], A66s, A11s, bStiff, + hStiff, &As_sens[3], &As_sens[0], + &DVsens[3], &DVsens[4]); + + // backpropagate stiffener A,D matrix sensitivities through stiffener DV + // ----------------------- + + TacsScalar t_s = this->stiffenerThick; + if (this->stiffenerThickLocalNum >= 0) { + int dvNum = this->stiffenerThickLocalNum; + + TacsScalar dDfactor_dthick = 0.25 * t_s * t_s; // d/dt(t^3/12) = t^2/4 + for (int ii = 0; ii < this->numStiffenerPlies; ii++) { + TacsScalar* Q = &(this->stiffenerQMats[ii * NUM_Q_ENTRIES]); + // backpropagate through the D matrix + dfdx[dvNum] += scale * dDfactor_dthick * this->stiffenerPlyFracs[ii] * + (Ds_sens[0] * Q[0] + Ds_sens[2] * Q[3] + + Ds_sens[1] * Q[1] + Ds_sens[3] * Q[5]); + // backpropagate through the A matrix + dfdx[dvNum] += scale * this->stiffenerPlyFracs[ii] * + (As_sens[0] * Q[0] + As_sens[2] * Q[3] + + As_sens[1] * Q[1] + As_sens[3] * Q[5]); + } + } + + // backpropagate stiffener A,D matrix sens through stiff ply DVs + // ----------------------- + for (int plyNum = 0; plyNum < this->numStiffenerPlies; plyNum++) { + int dvNum = this->stiffenerPlyFracLocalNums[plyNum]; + if (dvNum >= 0) { + const TacsScalar* Q = &(this->stiffenerQMats[plyNum * NUM_Q_ENTRIES]); + dfdx[dvNum] += scale * (t_s * t_s * t_s / 12.0) * + (Ds_sens[0] * Q[0] + Ds_sens[2] * Q[3] + + Ds_sens[1] * Q[1] + Ds_sens[3] * Q[5]); + dfdx[dvNum] += scale * t_s * + (As_sens[0] * Q[0] + As_sens[2] * Q[3] + + As_sens[1] * Q[1] + As_sens[3] * Q[5]); + } + } + + // 2,3,4 - backpropagate remaining DV sens into dfdx + // -------------------------------------------------- + + // recall DV sens [0 - panel length, 1 - stiff pitch, 2 - panel thick, + // 3 - stiff height, 4 - stiff thick, 5 - panel width] + + if (this->panelLengthLocalNum >= 0) { + dfdx[this->panelLengthLocalNum] += scale * DVsens[0]; + } + if (this->stiffenerPitchLocalNum >= 0) { + dfdx[this->stiffenerPitchLocalNum] += scale * DVsens[1]; + } + if (this->panelThickLocalNum >= 0) { + dfdx[this->panelThickLocalNum] += scale * DVsens[2]; + } + if (this->stiffenerHeightLocalNum >= 0) { + dfdx[this->stiffenerHeightLocalNum] += scale * DVsens[3]; + } + if (this->stiffenerThickLocalNum >= 0) { + dfdx[this->stiffenerThickLocalNum] += scale * DVsens[4]; + } + if (this->panelWidthLocalNum >= 0) { + dfdx[this->panelWidthLocalNum] += scale * DVsens[5]; + } + + } else { // CPTstiffenerCrippling = false + // the DOD experimental stiffener crippling solution from Ali's superclass + return TACSBladeStiffenedShellConstitutive::addStiffenerCripplingDVSens( + scale, stiffenerStrain, dfdx); + } +} + +// ============================================================================== +// HELPER ROUTINES +// ============================================================================== +void TACSGPBladeStiffenedShellConstitutive::computePanelGlobalBucklingStiffness( + const TacsScalar E1p, const TacsScalar zn, TacsScalar* D1) { + TacsScalar Ap, Ip, tp, sp, tp3; + tp = this->panelThick; + tp3 = tp * tp * tp; + sp = this->stiffenerPitch; + Ap = tp * sp; + Ip = sp * tp3 / 12.0; + + // compute panel D11 with overall centroid from panel and stiffener (so don't + // include stiffener D11 here) + *D1 = (E1p * (Ip + Ap * zn * zn)) / sp; + + // don't shift centroid of D22 to be consistent with stiffness matrix + + // ignoring this value for now => should maybe add this term back in later and + // compare results? + // // --- Twisting stiffness --- + // // Compute the shear modulus weighted centroid of the panel and stiffener + // TacsScalar zg = 0.25 * QStiffener[5] * As * (-0.5 * tp + zs) / + // (QStiffener[5] * As + QPanel[5] * Ap); + // *D3 = (QPanel[5] * (Jp + Ap * zg * zg)) / ps; +} + +void TACSGPBladeStiffenedShellConstitutive:: + computePanelGlobalBucklingStiffnessSens( + const TacsScalar D1Sens, const TacsScalar E1p, const TacsScalar zn, + TacsScalar* spitchSens, TacsScalar* pthickSens, TacsScalar* E1pSens, + TacsScalar* znSens) { + TacsScalar Ap, Ip, tp, sp, tp3; + tp = this->panelThick; + tp3 = tp * tp * tp; + sp = this->stiffenerPitch; + Ap = tp * sp; + Ip = sp * tp3 / 12.0; + + TacsScalar D1; + computePanelGlobalBucklingStiffness(E1p, zn, &D1); + + // now backpropagate derivatives to E1p, zn + *E1pSens += D1Sens * D1 / E1p; + *znSens += D1Sens * E1p * Ap * 2.0 * zn / sp; + + // backprop through intermediate vars + TacsScalar IpSens = D1Sens * E1p / sp; + *spitchSens += IpSens * tp3 / 12.0; + *pthickSens += IpSens * Ip * 3.0 / tp; + + TacsScalar ApSens = D1Sens * E1p * zn * zn / sp; + *spitchSens += ApSens * tp; + *pthickSens += ApSens * sp; + + // backprop to DVs + *spitchSens += D1Sens * D1 * -1.0 / sp; +} + +// Retrieve the design variable for plotting purposes +TacsScalar TACSGPBladeStiffenedShellConstitutive::evalDesignFieldValue( + int elemIndex, const double pt[], const TacsScalar X[], int index) { + if (writeDVmode == 0) { + switch (index) { + case 0: + return this->computeEffectiveThickness(); + case 1: + // return this->computeEffectiveBendingThickness(); + return this->panelWidth; + case 2: + return this->panelLength; + case 3: + return this->stiffenerPitch; + case 4: + return this->panelThick; + case 5: + return this->stiffenerHeight; + case 6: + return this->stiffenerThick; + } + } else if (writeDVmode == 1) { + // view the non-dimensional parameters + + TacsScalar panelStiffness[NUM_TANGENT_STIFFNESS_ENTRIES]; + this->computePanelStiffness(panelStiffness); + const TacsScalar *Ap, *Dp; + TacsScalar D11p; //, D66p; + this->extractTangentStiffness(panelStiffness, &Ap, NULL, &Dp, NULL, NULL); + // compute effective moduli, overall centroid + TacsScalar E1s, E1p, _; + this->computeEffectiveModulii(this->numPanelPlies, this->panelQMats, + this->panelPlyFracs, &E1p, &_); + this->computeEffectiveModulii(this->numStiffenerPlies, this->stiffenerQMats, + this->stiffenerPlyFracs, &E1s, &_); + TacsScalar zn = this->computeOverallCentroid(E1p, E1s); + + computePanelGlobalBucklingStiffness(E1p, zn, &D11p); + + // Compute panel dimensions, material props and non-dimensional parameters + TacsScalar N1CritGlobal, N12CritGlobal; + // TacsScalar D11p = Dp[0]; + TacsScalar D12p = Dp[1], D66p = Dp[5], D22p = Dp[3]; + TacsScalar A11p = Ap[0], A66p = Ap[5]; + TacsScalar delta, rho0, xi, gamma, a, b, zeta; + a = this->panelLength; + b = this->panelWidth; + delta = computeStiffenerAreaRatio(E1p, E1s); + rho0 = computeAffineAspectRatio(D11p, D22p, a, b); + xi = computeLaminateIsotropy(D11p, D22p, D12p, D66p); + gamma = computeStiffenerStiffnessRatio(D11p, E1s, zn); + zeta = computeTransverseShearParameter(A66p, A11p, b, this->panelThick); + + TacsScalar SAR = this->stiffenerHeight / this->stiffenerThick; + + switch (index) { + case 0: + return this->stiffenerPitch; + case 1: + return SAR; + case 2: + return delta; + case 3: + return rho0; + case 4: + return xi; + case 5: + return gamma; + case 6: + return zeta; + } + } + + return 0.0; +} + +void TACSGPBladeStiffenedShellConstitutive::computeStiffenerCripplingStiffness( + TacsScalar C[]) { + TacsScalar* A = &C[0]; + TacsScalar* B = &C[6]; + TacsScalar* D = &C[12]; + TacsScalar* As = &C[18]; + + // --- Zero out the C matrix --- + memset(C, 0, this->NUM_TANGENT_STIFFNESS_ENTRIES * sizeof(TacsScalar)); + + // Compute the smeared laminate properties + TacsScalar QStiff[this->NUM_Q_ENTRIES], ABarStiff[this->NUM_ABAR_ENTRIES]; + + this->computeSmearedStiffness(this->numStiffenerPlies, this->stiffenerQMats, + this->stiffenerAbarMats, + this->stiffenerPlyFracs, QStiff, ABarStiff); + + // Add the panel's contributions to the A and D matrices + TacsScalar t = this->stiffenerThick; + TacsScalar DFactor = t * t * t / 12.0; + + for (int ii = 0; ii < NUM_Q_ENTRIES; ii++) { + A[ii] += t * QStiff[ii]; + D[ii] += DFactor * QStiff[ii]; + } + + // Add the pane;'s contribution to the transverse shear matrix + for (int ii = 0; ii < NUM_ABAR_ENTRIES; ii++) { + As[ii] += t * QStiff[ii] * this->kcorr; + } + + // Add the drill stiffness + C[21] = DRILLING_REGULARIZATION * 0.5 * (As[0] + As[2]); +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::computeStiffenerInPlaneLoad( + const TacsScalar stiffenerStrain[], TacsScalar* A11s) { + int n = TACSBeamConstitutive::NUM_TANGENT_STIFFNESS_ENTRIES; + TacsScalar C[n]; + memset(C, 0, n * sizeof(TacsScalar)); + this->computeStiffenerStiffness(C); + + // divide out the height (width of stiffener when viewed as vertical panel) + // this way N11 = load/width = stress * thickness is still satisfied + *A11s = C[0] / this->stiffenerHeight; + + // return compressive strain * the A11 plate constant of the stiffener + return -stiffenerStrain[0] * (*A11s); +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeStiffenerInPlaneLoadSens( + const TacsScalar scale, const TacsScalar stiffenerStrain[], + const TacsScalar dN11_stiff, TacsScalar dfdx[]) { + int n = TACSBeamConstitutive::NUM_TANGENT_STIFFNESS_ENTRIES; + TacsScalar C[n]; + memset(C, 0, n * sizeof(TacsScalar)); + this->computeStiffenerStiffness(C); + + // get intermediate quantities needed for jacobian dN11_stiff/dx + TacsScalar E, _; + this->computeEffectiveModulii(this->numStiffenerPlies, this->stiffenerQMats, + this->stiffenerPlyFracs, &E, &_); + + // backpropgate to N11_bar = df/dN11_stiff + TacsScalar N11_stiff = this->computeStiffenerInPlaneLoad(stiffenerStrain, &_); + TacsScalar N11_bar = scale * dN11_stiff; + + // derivatives through E the effective modulus + // includes stiffener ply fraction DVs only + for (int plyNum = 0; plyNum < this->numStiffenerPlies; plyNum++) { + int dvNum = + this->stiffenerPlyFracLocalNums[plyNum] - this->stiffenerDVStartNum; + if (dvNum >= 0) { + const TacsScalar* Q = &(this->stiffenerQMats[plyNum * NUM_Q_ENTRIES]); + TacsScalar ply_frac_jac = + (Q[0] - Q[1] * Q[1] / Q[3]); // dE1p / dply_frac[i] + TacsScalar E1s_jac = N11_stiff / E; + dfdx[dvNum] += N11_bar * E1s_jac * ply_frac_jac; + } + } + + // derivatives through A11s but not effective modulus part + // includes stiffener thick (height cancels out b.c. in-plane load) + if (this->stiffenerThickLocalNum >= 0) { + int dvNum = this->stiffenerThickLocalNum - this->stiffenerDVStartNum; + // use power-series rule since N11_stiff propto sthick^1 + // if f = A * x, then df/dx = A = f / x (useful trick for fewer steps) + TacsScalar sthick_jac = N11_stiff / this->stiffenerThick; + dfdx[dvNum] += N11_bar * sthick_jac; + } + + // no longer consider derivatives directly for the strain here. + + // return usual forward analysis output if desired + return N11_stiff; +} + +// Retrieve the global design variable numbers +int TACSGPBladeStiffenedShellConstitutive::getDesignVarNums(int elemIndex, + int dvLen, + int dvNums[]) { + TACSBladeStiffenedShellConstitutive::getDesignVarNums(elemIndex, dvLen, + dvNums); + if (dvNums && dvLen >= this->numDesignVars) { + if (this->panelWidthNum >= 0) { + dvNums[this->panelWidthLocalNum] = panelWidthNum; + } + } + return numDesignVars; +} + +// Set the element design variable from the design vector +int TACSGPBladeStiffenedShellConstitutive::setDesignVars( + int elemIndex, int dvLen, const TacsScalar dvs[]) { + TACSBladeStiffenedShellConstitutive::setDesignVars(elemIndex, dvLen, dvs); + if (dvLen >= this->numDesignVars) { + if (this->panelWidthNum >= 0) { + this->panelWidth = dvs[this->panelWidthLocalNum]; + } + } + + // NOTE : this is a very important step => here we reset the save on all + // computed GP models so they recalculate and compute their new values. + if (this->panelGPs) { + this->panelGPs->resetSavedData(); + } + + return this->numDesignVars; +} + +// Get the element design variables values +int TACSGPBladeStiffenedShellConstitutive::getDesignVars(int elemIndex, + int dvLen, + TacsScalar dvs[]) { + TACSBladeStiffenedShellConstitutive::getDesignVars(elemIndex, dvLen, dvs); + if (dvLen >= this->numDesignVars) { + if (this->panelWidthNum >= 0) { + dvs[this->panelWidthLocalNum] = this->panelWidth; + } + } + return this->numDesignVars; +} + +// Get the lower and upper bounds for the design variable values +int TACSGPBladeStiffenedShellConstitutive::getDesignVarRange(int elemIndex, + int dvLen, + TacsScalar lb[], + TacsScalar ub[]) { + TACSBladeStiffenedShellConstitutive::getDesignVarRange(elemIndex, dvLen, lb, + ub); + if (dvLen >= this->numDesignVars) { + if (this->panelWidthNum >= 0) { + lb[this->panelWidthLocalNum] = this->panelWidthLowerBound; + ub[this->panelWidthLocalNum] = this->panelWidthUpperBound; + } + } + return this->numDesignVars; +} + +// ============================================================================== +// Buckling functions +// ============================================================================== + +TacsScalar TACSGPBladeStiffenedShellConstitutive::computeAffineAspectRatioSens( + const TacsScalar rho0sens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar a, const TacsScalar b, TacsScalar* D11sens, + TacsScalar* D22sens, TacsScalar* asens, TacsScalar* bsens) { + // compute the derivatives of the affine aspect ratio and return the affine + // aspect ratio + TacsScalar rho_0 = computeAffineAspectRatio(D11, D22, a, b); + // where rho_0 = a/b * (D22/D11)**0.25 + + // use power series rules d(x^p) = p * (x^p) / x to cleanly differentiate the + // expression + *D11sens += rho0sens * rho_0 * -0.25 / D11; + *D22sens += rho0sens * rho_0 * 0.25 / D22; + *asens += rho0sens * rho_0 / a; + *bsens += rho0sens * rho_0 * -1.0 / b; + + return rho_0; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::computeLaminateIsotropySens( + const TacsScalar xisens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar D12, const TacsScalar D66, TacsScalar* D11sens, + TacsScalar* D22sens, TacsScalar* D12sens, TacsScalar* D66sens) { + // compute the derivatives of the laminate isotropy xi + TacsScalar denominator = sqrt(D11 * D22); + TacsScalar xi = computeLaminateIsotropy(D11, D22, D12, D66); + // so that xi = (D12 + 2 * D66) / sqrt(D11*D22) + + // compute the sensitivities + *D12sens += xisens * 1.0 / denominator; + *D66sens += xisens * 2.0 / denominator; + *D11sens += xisens * -0.5 * xi / D11; + *D22sens += xisens * -0.5 * xi / D22; + + return xi; +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeGeneralizedPoissonsRatioSens( + const TacsScalar epssens, const TacsScalar D12, const TacsScalar D66, + TacsScalar* D12sens, TacsScalar* D66sens) { + // compute derivatives of the generalized poisson's ratio + TacsScalar eps = computeGeneralizedPoissonsRatio(D12, D66); + // where eps = (D12 + 2 * D66) / D12 + + *D12sens += epssens * eps * eps / D12 / D12 * 2.0 * D66; + *D66sens += epssens * -eps * eps * 2.0 / D12; + + return eps; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::computeStiffenerAreaRatio( + const TacsScalar E1p, const TacsScalar E1s) { + TacsScalar As = this->computeStiffenerArea(); + return E1s * As / (E1p * this->stiffenerPitch * this->panelThick); +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::computeStiffenerAreaRatioSens( + const TacsScalar deltasens, const TacsScalar E1p, const TacsScalar E1s, + TacsScalar* sthickSens, TacsScalar* sheightSens, TacsScalar* spitchSens, + TacsScalar* pthickSens, TacsScalar* E1psens, TacsScalar* E1ssens) { + TacsScalar delta = this->computeStiffenerAreaRatio(E1p, E1s); + + *sthickSens += deltasens * delta / this->stiffenerThick; + *sheightSens += deltasens * delta / this->stiffenerHeight; + *spitchSens += deltasens * -1.0 * delta / this->stiffenerPitch; + *pthickSens += deltasens * -1.0 * delta / this->panelThick; + + *E1ssens += deltasens * delta / E1s; + *E1psens += deltasens * delta * -1.0 / E1p; + + return delta; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::computeOverallCentroid( + const TacsScalar E1p, const TacsScalar E1s) { + TacsScalar Ap, As, zs, tp, ps; + tp = this->panelThick; + ps = this->stiffenerPitch; + Ap = tp * ps; + As = this->computeStiffenerArea(); + zs = this->computeStiffenerCentroidHeight(); + + TacsScalar zn = E1s * As * (-0.5 * tp - zs) / (E1s * As + E1p * Ap); + return zn; +} + +void TACSGPBladeStiffenedShellConstitutive::computeOverallCentroidSens( + const TacsScalar znSens, const TacsScalar E1p, const TacsScalar E1s, + TacsScalar* sthickSens, TacsScalar* sheightSens, TacsScalar* pthickSens, + TacsScalar* spitchSens, TacsScalar* E1pSens, TacsScalar* E1sSens) { + // compute numerator and denominators + TacsScalar Ap, As, zs, tp, sp; + tp = this->panelThick; + sp = this->stiffenerPitch; + Ap = tp * sp; + As = this->computeStiffenerArea(); + zs = this->computeStiffenerCentroidHeight(); + + TacsScalar num = E1s * As * (-0.5 * tp - zs); + TacsScalar den = E1s * As + E1p * Ap; + TacsScalar zn = num / den; + + // backprop to num, den + TacsScalar numSens = znSens / den; + TacsScalar denSens = znSens * zn * -1.0 / den; + + // backprop to any direct DVs or output sens + *E1sSens += numSens * num / E1s + denSens * As; + *E1pSens += denSens * Ap; + *pthickSens += numSens * E1s * As * -0.5; + + // backprop to intermediate quantities + TacsScalar ApSens = denSens * E1p; + TacsScalar AsSens = numSens * num / As + denSens * E1s; + TacsScalar zsSens = numSens * -1.0 * E1s * As; + + // backprop to DVs from intermediate quantities + *pthickSens += ApSens * sp; + *spitchSens += ApSens * tp; + + TacsScalar dAsdt, dAsdh; + computeStiffenerAreaSens(dAsdt, dAsdh); + *sthickSens += AsSens * dAsdt; + *sheightSens += AsSens * dAsdh; + + TacsScalar dzsdt, dzsdh; + computeStiffenerCentroidHeightSens(dzsdt, dzsdh); + *sthickSens += zsSens * dzsdt; + *sheightSens += zsSens * dzsdh; +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeStiffenerStiffnessRatio( + const TacsScalar D11, const TacsScalar E1s, const TacsScalar zn) { + TacsScalar As = this->computeStiffenerArea(); + TacsScalar Is = this->computeStiffenerIzz(); + TacsScalar zs = + -1.0 * this->computeStiffenerCentroidHeight(); // stiffener down into + // wing -Z direction + + TacsScalar Is_total = (Is + As * (zn - zs) * (zn - zs)); + return E1s * Is_total / D11 / this->stiffenerPitch; +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeStiffenerStiffnessRatioSens( + const TacsScalar gammaSens, const TacsScalar D11, const TacsScalar E1s, + const TacsScalar zn, TacsScalar* D11Sens, TacsScalar* sthickSens, + TacsScalar* sheightSens, TacsScalar* spitchSens, TacsScalar* E1sSens, + TacsScalar* znSens) { + // use power series rules and the forward state to differentiate + TacsScalar gamma = computeStiffenerStiffnessRatio(D11, E1s, zn); + + // backpropagate to DV level or base level without the Is term which is + // hardest + *D11Sens += gammaSens * -1.0 * gamma / D11; + *spitchSens += gammaSens * gamma * -1.0 / this->stiffenerPitch; + *E1sSens += gammaSens * gamma / E1s; + + // compute total Is + TacsScalar As = this->computeStiffenerArea(); + TacsScalar Is = this->computeStiffenerIzz(); + TacsScalar zs = + -1.0 * this->computeStiffenerCentroidHeight(); // stiffener down into + // wing -Z direction + + TacsScalar Is_total = (Is + As * (zn - zs) * (zn - zs)); + + // backpropagation to Is_tot + TacsScalar IstotSens = gammaSens * gamma / Is_total; + + // backpropagate to intermediate quantities As, Is, zs, zn + TacsScalar AsSens = IstotSens * (zn - zs) * (zn - zs); + TacsScalar IsSens = IstotSens; + TacsScalar zsSens = -1.0 * IstotSens * As * 2.0 * (zs - zn); + *znSens += IstotSens * As * 2.0 * (zn - zs); + + // backpropgate from intermediate quantities to DVs or material, etc. + TacsScalar dAdt, dAdh; + this->computeStiffenerAreaSens(dAdt, dAdh); + *sthickSens += AsSens * dAdt; + *sheightSens += AsSens * dAdh; + + TacsScalar dIdt, dIdh; + this->computeStiffenerIzzSens(dIdt, dIdh); + *sthickSens += IsSens * dIdt; + *sheightSens += IsSens * dIdh; + + TacsScalar dZdt, dZdh; + this->computeStiffenerCentroidHeightSens(dZdt, dZdh); + *sthickSens += zsSens * dZdt; + *sheightSens += zsSens * dZdh; + + return gamma; +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeTransverseShearParameter( + TacsScalar A66, TacsScalar A11, TacsScalar b, TacsScalar h) { + return A11 / A66 * (h / b) * (h / b); +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeTransverseShearParameterSens( + const TacsScalar zetasens, const TacsScalar A66, const TacsScalar A11, + const TacsScalar b, const TacsScalar h, TacsScalar* A66sens, + TacsScalar* A11sens, TacsScalar* bsens, TacsScalar* hsens) { + TacsScalar zeta = computeTransverseShearParameter(A66, A11, b, h); + TacsScalar dzeta = zetasens * zeta; + + *A66sens += dzeta * -1.0 / A66; + *A11sens += dzeta / A11; + *bsens += dzeta * -2.0 / b; + *hsens += dzeta * 2.0 / h; + + return zeta; +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeCriticalGlobalAxialLoad( + const TacsScalar D11, const TacsScalar D22, const TacsScalar b, + const TacsScalar delta, const TacsScalar rho_0, const TacsScalar xi, + const TacsScalar gamma, const TacsScalar zeta) { + if (this->getAxialGP()) { + // use Gaussian processes to compute the critical global axial load + TacsScalar dim_factor = + M_PI * M_PI * sqrt(D11 * D22) / b / b / (1.0 + delta); + TacsScalar one = 1.0; + XtestAxial[0] = log(one + xi); + XtestAxial[1] = log(rho_0); + XtestAxial[2] = log(one + gamma); + XtestAxial[3] = log(one + 1000.0 * zeta); + TacsScalar nd_factor = + exp(this->panelGPs->predictMeanTestData(0, XtestAxial)); + return dim_factor * nd_factor; + + } else { + // use the CPT closed-form solution to compute the critical global axial + // load + TacsScalar neg_N11crits[this->NUM_CF_MODES]; + for (int _m1 = 1; _m1 < this->NUM_CF_MODES + 1; _m1++) { + TacsScalar dim_factor = + M_PI * M_PI * sqrt(D11 * D22) / b / b / (1.0 + delta); + TacsScalar m1 = _m1; + TacsScalar nondim_factor = (1.0 + gamma) * pow(m1 / rho_0, 2.0) + + pow(m1 / rho_0, -2.0) + 2.0 * xi; + neg_N11crits[_m1 - 1] = + -1.0 * dim_factor * nondim_factor; // negated only because we have to + // do KS min aggregate later + } + + // compute KS aggregation for -N11crit for each mode then negate again + // (because we want minimum N11crit so maximize negative N11crit) + TacsScalar neg_N11crit = + ksAggregation(neg_N11crits, this->NUM_CF_MODES, this->ksWeight); + + TacsScalar dim_factor = + M_PI * M_PI * sqrt(D11 * D22) / b / b / (1.0 + delta); + TacsScalar N11crStar = -1.0 * neg_N11crit / dim_factor; + // printf("N11cr* = %.4f\n", N11crStar); + return -1.0 * neg_N11crit; + } +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::nondimCriticalGlobalAxialLoad( + const TacsScalar rho_0, const TacsScalar xi, const TacsScalar gamma, + const TacsScalar zeta) { + if (this->getAxialGP()) { + // use Gaussian processes to compute the critical global axial load + TacsScalar dim_factor = 1.0; + TacsScalar one = 1.0; + XtestAxial[0] = log(one + xi); + XtestAxial[1] = log(rho_0); + XtestAxial[2] = log(one + gamma); + XtestAxial[3] = log(one + 1000.0 * zeta); + + // don't need to use the saved data here since this routine is only meant to + // be called on a single constitutive object (not a whole mesh with O(1e4) + // const objects) + TacsScalar nd_factor = + exp(this->getAxialGP()->predictMeanTestData(XtestAxial)); + return dim_factor * nd_factor; + + } else { + // use the CPT closed-form solution to compute the critical global axial + // load + TacsScalar neg_N11crits[this->NUM_CF_MODES]; + for (int _m1 = 1; _m1 < this->NUM_CF_MODES + 1; _m1++) { + TacsScalar dim_factor = 1.0; + TacsScalar m1 = _m1; + TacsScalar nondim_factor = (1.0 + gamma) * pow(m1 / rho_0, 2.0) + + pow(m1 / rho_0, -2.0) + 2.0 * xi; + neg_N11crits[_m1 - 1] = + -1.0 * dim_factor * nondim_factor; // negated only because we have to + // do KS min aggregate later + } + + // compute KS aggregation for -N11crit for each mode then negate again + // (because we want minimum N11crit so maximize negative N11crit) + TacsScalar neg_N11crit = + ksAggregation(neg_N11crits, this->NUM_CF_MODES, this->ksWeight); + return -1.0 * neg_N11crit; + } +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeCriticalGlobalAxialLoadSens( + const TacsScalar N1sens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar b, const TacsScalar delta, const TacsScalar rho_0, + const TacsScalar xi, const TacsScalar gamma, const TacsScalar zeta, + TacsScalar* D11sens, TacsScalar* D22sens, TacsScalar* bsens, + TacsScalar* deltasens, TacsScalar* rho_0sens, TacsScalar* xisens, + TacsScalar* gammasens, TacsScalar* zetasens) { + if (this->getAxialGP()) { + // use Gaussian processes to compute the critical global axial load + TacsScalar dim_factor = + M_PI * M_PI * sqrt(D11 * D22) / b / b / (1.0 + delta); + TacsScalar one = 1.0; + XtestAxial[0] = log(one + xi); + XtestAxial[1] = log(rho_0); + XtestAxial[2] = log(one + gamma); + XtestAxial[3] = log(one + 1000.0 * zeta); + TacsScalar arg = this->panelGPs->predictMeanTestData(0, XtestAxial); + TacsScalar nondim_factor = exp(arg); + TacsScalar output = dim_factor * nondim_factor; + + // compute sensitivities backwards propagated out of the GP + // back to the nondim parameter inputs, (this part differentiates the + // nondim_factor) + TacsScalar Ysens = N1sens * output; + this->panelGPs->predictMeanTestDataSens(0, Ysens, XtestAxial, + XtestAxialSens); + *xisens += + XtestAxialSens[0] / (one + xi); // chain rule dlog(one + xi)/dxi = 1/xi + *rho_0sens += XtestAxialSens[1] / rho_0; + *gammasens += XtestAxialSens[2] / (1.0 + gamma); + *zetasens += XtestAxialSens[3] / (one + 1000.0 * zeta) * 1000.0; + + // compute the sensivities of inputs in the dimensional constant + // (this part differentiates the dim factor) + *D11sens += Ysens * 0.5 / D11; + *D22sens += Ysens * 0.5 / D22; + *bsens += Ysens * -2.0 / b; + *deltasens += Ysens * -1.0 / (1.0 + delta); + + return output; + + } else { + // use the CPT closed-form solution to compute the critical global axial + // load forward analysis part here + TacsScalar neg_N11crits[this->NUM_CF_MODES]; + for (int _m1 = 1; _m1 < this->NUM_CF_MODES + 1; _m1++) { + TacsScalar dim_factor = + M_PI * M_PI * sqrt(D11 * D22) / b / b / (1.0 + delta); + TacsScalar m1 = _m1; + TacsScalar nondim_factor = (1.0 + gamma) * pow(m1 / rho_0, 2.0) + + pow(m1 / rho_0, -2.0) + 2.0 * xi; + neg_N11crits[_m1 - 1] = + -1.0 * dim_factor * nondim_factor; // negated only because we have to + // do KS min aggregate later + } + + // compute KS aggregation sensitivity + TacsScalar neg_N11crits_sens[this->NUM_CF_MODES]; + TacsScalar neg_N11crit = ksAggregationSens( + neg_N11crits, this->NUM_CF_MODES, this->ksWeight, neg_N11crits_sens); + + // compute sensitivities here + for (int _m1 = 1; _m1 < this->NUM_CF_MODES + 1; _m1++) { + // apply output sens + neg_N11crits_sens[_m1 - 1] *= N1sens; + // apply -1 last step + neg_N11crits_sens[_m1 - 1] *= -1.0; + + // convert to double/cmplx type here + TacsScalar m1 = _m1; + + // forward analysis states + TacsScalar dim_factor = + M_PI * M_PI * sqrt(D11 * D22) / b / b / (1.0 + delta); + TacsScalar nondim_factor = (1.0 + gamma) * pow(m1 / rho_0, 2.0) + + pow(m1 / rho_0, -2.0) + 2.0 * xi; + neg_N11crits[_m1 - 1] = + -1.0 * dim_factor * nondim_factor; // negated only because we have to + // do KS min aggregate later + + // update sensitivities (left factor is dKS/dv_i, right factor is dv_i / + // dx) + *D11sens += + neg_N11crits_sens[_m1 - 1] * (0.5 * neg_N11crits[_m1 - 1] / D11); + *D22sens += + neg_N11crits_sens[_m1 - 1] * (0.5 * neg_N11crits[_m1 - 1] / D22); + *bsens += neg_N11crits_sens[_m1 - 1] * (-2.0 * neg_N11crits[_m1 - 1] / b); + *deltasens += neg_N11crits_sens[_m1 - 1] * + (-1.0 * neg_N11crits[_m1 - 1] / (1.0 + delta)); + *rho_0sens += neg_N11crits_sens[_m1 - 1] * -dim_factor * + ((1.0 + gamma) * -2.0 * pow(m1 / rho_0, 2.0) / rho_0 + + pow(m1 / rho_0, -2.0) * 2.0 / rho_0); + *xisens += neg_N11crits_sens[_m1 - 1] * -dim_factor * 2.0; + *gammasens += + neg_N11crits_sens[_m1 - 1] * -dim_factor * pow(m1 / rho_0, 2.0); + } + return -1.0 * neg_N11crit; + } +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::computeCriticalLocalAxialLoad( + const TacsScalar D11, const TacsScalar D22, const TacsScalar rho_0, + const TacsScalar xi, const TacsScalar zeta) { + if (this->getAxialGP()) { + // use Gaussian processes to compute the critical global axial load + TacsScalar dim_factor = M_PI * M_PI * sqrt(D11 * D22) / + this->stiffenerPitch / this->stiffenerPitch; + TacsScalar one = 1.0; + XtestAxial[0] = log(one + xi); + XtestAxial[1] = log(rho_0); + XtestAxial[2] = + 0.0; // log(1+gamma) = 0 since gamma=0 for unstiffened panel + XtestAxial[3] = log(one + 1000.0 * zeta); + TacsScalar nd_factor = + exp(this->panelGPs->predictMeanTestData(1, XtestAxial)); + return dim_factor * nd_factor; + + } else { + // use the CPT closed-form solution to compute the critical global axial + // load + TacsScalar neg_N11crits[this->NUM_CF_MODES]; + for (int _m1 = 1; _m1 < this->NUM_CF_MODES + 1; _m1++) { + TacsScalar dim_factor = M_PI * M_PI * sqrt(D11 * D22) / + this->stiffenerPitch / this->stiffenerPitch; + // convert to double/cmplx type here + TacsScalar m1 = _m1; + TacsScalar nondim_factor = + pow(m1 / rho_0, 2.0) + pow(m1 / rho_0, -2.0) + 2.0 * xi; + neg_N11crits[_m1 - 1] = + -1.0 * dim_factor * nondim_factor; // negated only because we have to + // do KS min aggregate later + } + + // compute KS aggregation for -N11crit for each mode then negate again + // (because we want minimum N11crit so maximize negative N11crit) + TacsScalar neg_N11crit = + ksAggregation(neg_N11crits, this->NUM_CF_MODES, this->ksWeight); + return -1.0 * neg_N11crit; + } +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::nondimCriticalLocalAxialLoad( + const TacsScalar rho_0, const TacsScalar xi, const TacsScalar zeta) { + if (this->getAxialGP()) { + // use Gaussian processes to compute the critical global axial load + TacsScalar dim_factor = 1.0; + TacsScalar one = 1.0; + XtestAxial[0] = log(one + xi); + XtestAxial[1] = log(rho_0); + XtestAxial[2] = + 0.0; // log(1+gamma) = 0 since gamma=0 for unstiffened panel + XtestAxial[3] = log(one + 1000.0 * zeta); + // don't need to use the saved data here since this routine is only meant to + // be called on a single constitutive object (not a whole mesh with O(1e4) + // const objects) + TacsScalar nd_factor = + exp(this->getAxialGP()->predictMeanTestData(XtestAxial)); + return dim_factor * nd_factor; + + } else { + // use the CPT closed-form solution to compute the critical global axial + // load + TacsScalar neg_N11crits[this->NUM_CF_MODES]; + for (int _m1 = 1; _m1 < this->NUM_CF_MODES + 1; _m1++) { + TacsScalar dim_factor = 1.0; + // convert to double/cmplx type here + TacsScalar m1 = _m1; + TacsScalar nondim_factor = + pow(m1 / rho_0, 2.0) + pow(m1 / rho_0, -2.0) + 2.0 * xi; + neg_N11crits[_m1 - 1] = + -1.0 * dim_factor * nondim_factor; // negated only because we have to + // do KS min aggregate later + } + + // compute KS aggregation for -N11crit for each mode then negate again + // (because we want minimum N11crit so maximize negative N11crit) + TacsScalar neg_N11crit = + ksAggregation(neg_N11crits, this->NUM_CF_MODES, this->ksWeight); + return -1.0 * neg_N11crit; + } +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeCriticalLocalAxialLoadSens( + const TacsScalar N1sens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar rho_0, const TacsScalar xi, const TacsScalar zeta, + TacsScalar* D11sens, TacsScalar* D22sens, TacsScalar* spitchsens, + TacsScalar* rho_0sens, TacsScalar* xisens, TacsScalar* zetasens) { + if (this->getAxialGP()) { + // use Gaussian processes to compute the critical global axial load + TacsScalar dim_factor = M_PI * M_PI * sqrt(D11 * D22) / + this->stiffenerPitch / this->stiffenerPitch; + TacsScalar one = 1.0; + XtestAxial[0] = log(one + xi); + XtestAxial[1] = log(rho_0); + XtestAxial[2] = + 0.0; // log(1+gamma) = 0 since gamma=0 for unstiffened panel + XtestAxial[3] = log(one + 1000.0 * zeta); + + TacsScalar arg = this->panelGPs->predictMeanTestData(1, XtestAxial); + TacsScalar nondim_factor = exp(arg); + TacsScalar output = dim_factor * nondim_factor; + + // backwards propagate sensitivities out of the axialGP model to nondim + // params, (this part differentiates the nondim_factor) + TacsScalar Ysens = N1sens * output; + this->panelGPs->predictMeanTestDataSens(1, Ysens, XtestAxial, + XtestAxialSens); + + *xisens += XtestAxialSens[0] / (one + xi); + *rho_0sens += + XtestAxialSens[1] / rho_0; // chain rule dlog(rho_0) / drho_0 = 1/rho_0 + *zetasens += XtestAxialSens[3] / (one + 1000.0 * zeta) * 1000.0; + + // backpropagate the dimensional factor terms out to the material and + // geometric DVs (this part differentiates the dim_factor) + *D11sens += Ysens * 0.5 / D11; + *D22sens += Ysens * 0.5 / D22; + *spitchsens += Ysens * -2.0 / this->stiffenerPitch; + + // return the final forward analysis output + return output; + + } else { + // use the CPT closed-form solution to compute the critical global axial + // load forward analysis part here + TacsScalar neg_N11crits[this->NUM_CF_MODES]; + for (int _m1 = 1; _m1 < this->NUM_CF_MODES + 1; _m1++) { + TacsScalar dim_factor = M_PI * M_PI * sqrt(D11 * D22) / + this->stiffenerPitch / this->stiffenerPitch; + TacsScalar m1 = _m1; + TacsScalar nondim_factor = + pow(m1 / rho_0, 2.0) + pow(m1 / rho_0, -2.0) + 2.0 * xi; + neg_N11crits[_m1 - 1] = + -1.0 * dim_factor * nondim_factor; // negated only because we have to + // do KS min aggregate later + } + + // compute KS aggregation sensitivity + TacsScalar neg_N11crits_sens[this->NUM_CF_MODES]; + TacsScalar neg_N11crit = ksAggregationSens( + neg_N11crits, this->NUM_CF_MODES, this->ksWeight, neg_N11crits_sens); + + // compute sensitivities here + for (int _m1 = 1; _m1 < this->NUM_CF_MODES + 1; _m1++) { + TacsScalar m1 = _m1; + // backpropagate through the output + neg_N11crits_sens[_m1 - 1] *= N1sens; + // apply -1 last step + neg_N11crits_sens[_m1 - 1] *= -1.0; + + // forward analysis states + TacsScalar dim_factor = M_PI * M_PI * sqrt(D11 * D22) / + this->stiffenerPitch / this->stiffenerPitch; + TacsScalar nondim_factor = + pow(m1 / rho_0, 2.0) + pow(m1 / rho_0, -2.0) + 2.0 * xi; + neg_N11crits[_m1 - 1] = + -1.0 * dim_factor * nondim_factor; // negated only because we have to + // do KS min aggregate later + + // update sensitivities (left factor is dKS/dv_i, right factor is dv_i / + // dx) + *D11sens += + neg_N11crits_sens[_m1 - 1] * (0.5 * neg_N11crits[_m1 - 1] / D11); + *D22sens += + neg_N11crits_sens[_m1 - 1] * (0.5 * neg_N11crits[_m1 - 1] / D22); + *spitchsens += neg_N11crits_sens[_m1 - 1] * + (-2.0 * neg_N11crits[_m1 - 1] / this->stiffenerPitch); + *rho_0sens += neg_N11crits_sens[_m1 - 1] * -dim_factor * + (-2.0 * pow(m1 / rho_0, 2.0) / rho_0 + + pow(m1 / rho_0, -2.0) * 2.0 / rho_0); + *xisens += neg_N11crits_sens[_m1 - 1] * -dim_factor * 2.0; + //*zetasens is unchanged + } + return -1.0 * neg_N11crit; + } +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeCriticalGlobalShearLoad( + const TacsScalar D11, const TacsScalar D22, const TacsScalar b, + const TacsScalar rho_0, const TacsScalar xi, const TacsScalar gamma, + const TacsScalar zeta) { + if (this->getShearGP()) { + // use Gaussian processes to compute the critical global shear load + TacsScalar dim_factor = + M_PI * M_PI * pow(D11 * D22 * D22 * D22, 0.25) / b / b; + TacsScalar one = 1.0; + XtestShear[0] = log(one + xi); + XtestShear[1] = log(rho_0); + XtestShear[2] = log( + one + gamma); // log(1+gamma) = 0 since gamma=0 for unstiffened panel + XtestShear[3] = log(one + 1000.0 * zeta); + TacsScalar nd_factor = + exp(this->panelGPs->predictMeanTestData(2, XtestShear)); + TacsScalar output = dim_factor * nd_factor; + return dim_factor * nd_factor; + + } else { + // use the CPT closed-form solution to compute the critical global axial + // load no mode switching in this solution.. (some error at low aspect + // ratios + TacsScalar lam1, lam2; // lam1bar, lam2bar values + nondimShearParams(xi, gamma, &lam1, &lam2); + TacsScalar dim_factor = + M_PI * M_PI * pow(D11 * D22 * D22 * D22, 0.25) / b / b; + TacsScalar nondim_factor = + (1.0 + pow(lam1, 4.0) + 6.0 * pow(lam1 * lam2, 2.0) + pow(lam2, 4.0) + + 2.0 * xi * (lam1 * lam1 + lam2 * lam2) + gamma) / + (2.0 * lam1 * lam1 * lam2); + // accounts for high and low ARs here + // smooth max of (1,rho_0^{-2}) + TacsScalar shear_geom[2]; + shear_geom[0] = 1.0; + shear_geom[1] = 1.0 / rho_0 / rho_0; + TacsScalar shear_geom_ks = ksAggregation(shear_geom, 2, this->ksWeight); + nondim_factor *= shear_geom_ks; + return dim_factor * + nondim_factor; // aka N12_crit from CPT closed-form solution + } +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::nondimCriticalGlobalShearLoad( + const TacsScalar rho_0, const TacsScalar xi, const TacsScalar gamma, + const TacsScalar zeta) { + if (this->getShearGP()) { + // use Gaussian processes to compute the critical global shear load + TacsScalar dim_factor = 1.0; + TacsScalar one = 1.0; + XtestShear[0] = log(one + xi); + XtestShear[1] = log(rho_0); + XtestShear[2] = log( + one + gamma); // log(1+gamma) = 0 since gamma=0 for unstiffened panel + XtestShear[3] = log(one + 1000.0 * zeta); + // don't need to use the saved data here since this routine is only meant to + // be called on a single constitutive object (not a whole mesh with O(1e4) + // const objects) + TacsScalar nd_factor = + exp(this->getShearGP()->predictMeanTestData(XtestShear)); + return dim_factor * nd_factor; + + } else { + // use the CPT closed-form solution to compute the critical global axial + // load no mode switching in this solution.. + // CPT closed-form solution now accurate for low and high ARs with some area + // at intermediate.. + TacsScalar lam1, lam2; // lam1bar, lam2bar values + nondimShearParams(xi, gamma, &lam1, &lam2); + TacsScalar dim_factor = 1.0; + TacsScalar nondim_factor = + (1.0 + pow(lam1, 4.0) + 6.0 * pow(lam1 * lam2, 2.0) + pow(lam2, 4.0) + + 2.0 * xi * (lam1 * lam1 + lam2 * lam2) + gamma) / + (2.0 * lam1 * lam1 * lam2); + + // accounts for high and low ARs here + // smooth max of (1,rho_0^{-2}) + TacsScalar shear_geom[2]; + shear_geom[0] = 1.0; + shear_geom[1] = 1.0 / rho_0 / rho_0; + TacsScalar shear_geom_ks = ksAggregation(shear_geom, 2, this->ksWeight); + nondim_factor *= shear_geom_ks; + + return dim_factor * + nondim_factor; // aka N12_crit from CPT closed-form solution + } +} + +void TACSGPBladeStiffenedShellConstitutive::nondimShearParams( + const TacsScalar xi, const TacsScalar gamma, TacsScalar* lam1bar, + TacsScalar* lam2bar) { + // need to iterate over lam2 with the Newton's method + TacsScalar lam2bar_sq = 0.0; // starting guess for lambda2_bar^2 + + TacsScalar init_resid = lam2Constraint(lam2bar_sq, xi, gamma); + // Newton iteration for lam2bar squared + int ct = 0; + // use rel tolerance of 1e-13 + while (abs(TacsRealPart(lam2Constraint(lam2bar_sq, xi, gamma)) / init_resid) > + 1e-13 && + ct < 50) { + lam2bar_sq -= lam2Constraint(lam2bar_sq, xi, gamma) / + lam2ConstraintDeriv(lam2bar_sq, xi, gamma); + ct += 1; + } + + // now compute lam1_bar, lam2_bar + *lam1bar = + pow(1.0 + 2.0 * lam2bar_sq * xi + lam2bar_sq * lam2bar_sq + gamma, 0.25); + *lam2bar = pow(lam2bar_sq, 0.5); +} +TacsScalar TACSGPBladeStiffenedShellConstitutive::lam2Constraint( + const TacsScalar lam2sq, const TacsScalar xi, const TacsScalar gamma) { + // compute the residual of the combined lam1bar, lam2bar constraint but on + // lam2bar^2 + // if x = lam2sq, and lam1sq = f(x), lam1^4 = f(x)^2 + // then we have: + + // f(x) = lam1^2(x) where x = lam2^2 + TacsScalar f = pow(1.0 + 2.0 * lam2sq * xi + lam2sq * lam2sq + gamma, 0.5); + TacsScalar term3 = + sqrt((3.0 + xi) / 9.0 + 4.0 / 3.0 * f * xi + 4.0 / 3.0 * f * f); + + return lam2sq + f + xi / 3.0 - term3; +} +TacsScalar TACSGPBladeStiffenedShellConstitutive::lam2ConstraintDeriv( + const TacsScalar lam2sq, const TacsScalar xi, const TacsScalar gamma) { + // compute the residual derivatives for the lam2bar constraint above w.r.t. + // the lam2sq input about a lam2sq input + // if x = lam2sq, and lam1sq = f(x), lam1^4 = f(x)^2 + // then we have: + + TacsScalar deriv = 1.0; + // f(x) = lam1^2(x) where x = lam2^2 + TacsScalar f = pow(1.0 + 2.0 * lam2sq * xi + lam2sq * lam2sq + gamma, 0.5); + // f'(x) = (x + xi) / f(x) + TacsScalar fp = (lam2sq + xi) / f; + deriv += fp; + + // term3 the sqrt term with many terms inside + TacsScalar term3 = + sqrt((3.0 + xi) / 9.0 + 4.0 / 3.0 * f * xi + 4.0 / 3.0 * f * f); + + // simplified chain rule over sqrt long-term expression (i.e. term3) + deriv -= 2.0 * fp / 3.0 / term3 * (xi + 2.0 * f); + return deriv; +} + +void TACSGPBladeStiffenedShellConstitutive::nondimShearParamsSens( + const TacsScalar xi, const TacsScalar gamma, TacsScalar* lam1bar, + TacsScalar* lam2bar, TacsScalar* dl1xi, TacsScalar* dl1gamma, + TacsScalar* dl2xi, TacsScalar* dl2gamma) { + // get the lam1, lam2 from Newton's method + TacsScalar lam1, lam2; + nondimShearParams(xi, gamma, &lam1, &lam2); + + // also send out the lam1bar, lam2bar again + *lam1bar = lam1; + *lam2bar = lam2; + + // differentiate the nonlinear constraints from nondimShearParam subroutine + // sys eqns [A,B;C,D] * [lam1bar_dot, lam2bar_dot] = [E,F] for each of the two + // derivatives + + TacsScalar y1 = 1.0 + 2.0 * lam2 * lam2 * xi + pow(lam2, 4.0) + gamma; + TacsScalar dy1lam2 = 4.0 * lam2 * xi + 4.0 * lam2 * lam2 * lam2; + TacsScalar dy1xi = 2.0 * lam2 * lam2; + TacsScalar dy1gamma = 1.0; + TacsScalar y2 = + (3.0 + xi) / 9.0 + 4.0 / 3.0 * (lam1 * lam1 * xi + pow(lam1, 4.0)); + TacsScalar dy2lam1 = 4.0 / 3.0 * (2.0 * lam1 * xi + 4.0 * lam1 * lam1 * lam1); + TacsScalar dy2xi = 1.0 / 9.0 + 4.0 / 3.0 * lam1 * lam1; + TacsScalar dy2gamma = 0.0; + + // first for the xi sensitivities + TacsScalar A1, B1, C1, D1, E1, F1; + A1 = 1.0; + B1 = -0.25 * lam1 / y1 * dy1lam2; + E1 = 0.25 * lam1 / y1 * dy1xi; + C1 = 2.0 * lam1 - 0.5 * pow(y2, -0.5) * dy2lam1; + D1 = 2.0 * lam2; + F1 = -1.0 / 3.0 + 0.5 * pow(y2, -0.5) * dy2xi; + *dl1xi = (D1 * E1 - B1 * F1) / (A1 * D1 - B1 * C1); + *dl2xi = (A1 * F1 - C1 * E1) / (A1 * D1 - B1 * C1); + + // then for the gamma sensitivities + TacsScalar A2, B2, C2, D2, E2, F2; + A2 = A1; + B2 = B1; + E2 = 0.25 * lam1 / y1 * dy1gamma; + C2 = C1; + D2 = D1; + F2 = 0.5 * pow(y2, -0.5) * dy2gamma; + *dl1gamma = (D2 * E2 - B2 * F2) / (A2 * D2 - B2 * C2); + *dl2gamma = (A2 * F2 - C2 * E2) / (A2 * D2 - B2 * C2); +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeCriticalGlobalShearLoadSens( + const TacsScalar N12sens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar b, const TacsScalar rho_0, const TacsScalar xi, + const TacsScalar gamma, const TacsScalar zeta, TacsScalar* D11sens, + TacsScalar* D22sens, TacsScalar* bsens, TacsScalar* rho_0sens, + TacsScalar* xisens, TacsScalar* gammasens, TacsScalar* zetasens) { + if (this->getShearGP()) { + // use Gaussian processes to compute the critical global shear load + TacsScalar dim_factor = + M_PI * M_PI * pow(D11 * D22 * D22 * D22, 0.25) / b / b; + TacsScalar one = 1.0; + XtestShear[0] = log(one + xi); + XtestShear[1] = log(rho_0); + XtestShear[2] = log( + one + gamma); // log(1+gamma) = 0 since gamma=0 for unstiffened panel + XtestShear[3] = log(one + 1000.0 * zeta); + TacsScalar arg = this->panelGPs->predictMeanTestData(2, XtestShear); + TacsScalar nondim_factor = exp(arg); + TacsScalar output = dim_factor * nondim_factor; + + // backwards propagate sensitivities out of the axialGP model to nondim + // params, (this part differentiates the nondim_factor) + TacsScalar Ysens = N12sens * output; + this->panelGPs->predictMeanTestDataSens(2, Ysens, XtestShear, + XtestShearSens); + + *xisens += XtestShearSens[0] / (one + xi); + *rho_0sens += + XtestShearSens[1] / rho_0; // chain rule dlog(rho_0) / drho_0 = 1/rho_0 + *gammasens += XtestShearSens[2] / (1.0 + gamma); + *zetasens += XtestShearSens[3] / (one + 1000.0 * zeta) * 1000.0; + + // backpropagate the dimensional factor terms out to the material and + // geometric DVs (this part differentiates the dim_factor) + *D11sens += Ysens * 0.25 / D11; + *D22sens += Ysens * 0.75 / D22; + *bsens += Ysens * -2.0 / b; + + // return the final forward analysis output + return output; + + } else { + // use the CPT closed-form solution to compute the critical global axial + // load no mode switching in this solution.. (only accurate for higher + // aspect ratios => hence the need for machine learning for the actual + // solution) + TacsScalar lam1, lam2; // lam1bar, lam2bar values + TacsScalar dl1xi, dl2xi, dl1gamma, dl2gamma; + + // compute the derivatives of the nondimensional constraints + nondimShearParamsSens(xi, gamma, &lam1, &lam2, &dl1xi, &dl1gamma, &dl2xi, + &dl2gamma); + + // compute forward analysis states involved in the N12crit load + TacsScalar dim_factor = + M_PI * M_PI * pow(D11 * D22 * D22 * D22, 0.25) / b / b; + TacsScalar num = + (1.0 + pow(lam1, 4.0) + 6.0 * pow(lam1 * lam2, 2.0) + pow(lam2, 4.0) + + 2.0 * xi * (lam1 * lam1 + lam2 * lam2) + gamma); + TacsScalar den = 2.0 * lam1 * lam1 * lam2; + TacsScalar nondim_factor = num / den; + + // accounts for high and low ARs here + // smooth max of (1,rho_0^{-2}) + TacsScalar shear_geom[2]; + shear_geom[0] = 1.0; + shear_geom[1] = 1.0 / rho_0 / rho_0; + TacsScalar shear_geom_ks = ksAggregation(shear_geom, 2, this->ksWeight); + + // final forward output + TacsScalar N12crit = dim_factor * nondim_factor * shear_geom_ks; + + // sensitivities for the non_dim factor + + TacsScalar dNDlam1 = + (4.0 * pow(lam1, 3.0) + 12.0 * lam1 * lam2 * lam2 + 4.0 * lam1 * xi) / + den - + num * 4.0 * lam1 * lam2 / den / den; + TacsScalar dNDlam2 = + (4.0 * pow(lam2, 3.0) + 12.0 * lam2 * lam1 * lam1 + 4.0 * lam2 * xi) / + den - + num * 2.0 * lam1 * lam1 / den / den; + + // compute KS aggregation sensitivity + TacsScalar shear_geom_sens[2]; + ksAggregationSens(shear_geom, 2, this->ksWeight, shear_geom_sens); + TacsScalar c_rho0_sens = shear_geom_sens[1] * shear_geom[1] * -2.0 / rho_0; + + // compute the overall sensitivities + *D11sens += N12sens * N12crit * 0.25 / D11; + *D22sens += N12sens * N12crit * 0.75 / D22; + *bsens += N12sens * N12crit * -2.0 / b; + *xisens += N12sens * dim_factor * + (dNDlam1 * dl1xi + dNDlam2 * dl2xi + + 2.0 * (lam1 * lam1 + lam2 * lam2) / den) * + shear_geom_ks; + *gammasens += N12sens * dim_factor * + (dNDlam1 * dl1gamma + dNDlam2 * dl2gamma + 1.0 / den) * + shear_geom_ks; + *rho_0sens += N12sens * c_rho0_sens * dim_factor * nondim_factor; + // *zetasens are unchanged in closed-form + + // return N12crit from closed-form solution + return N12crit; + } +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::computeCriticalLocalShearLoad( + const TacsScalar D11, const TacsScalar D22, const TacsScalar rho_0, + const TacsScalar xi, const TacsScalar zeta) { + if (this->getShearGP()) { + // use Gaussian processes to compute the critical global shear load + TacsScalar s_p = this->stiffenerPitch; + TacsScalar dim_factor = + M_PI * M_PI * pow(D11 * D22 * D22 * D22, 0.25) / s_p / s_p; + TacsScalar one = 1.0; + XtestShear[0] = log(one + xi); + XtestShear[1] = log(rho_0); + XtestShear[2] = + 0.0; // log(1+gamma) = 0 since gamma=0 for unstiffened panel + XtestShear[3] = log(one + 1000.0 * zeta); + TacsScalar nd_factor = + exp(this->panelGPs->predictMeanTestData(3, XtestShear)); + return dim_factor * nd_factor; + + } else { + // use the CPT closed-form solution to compute the critical global axial + // load no mode switching in this solution.. (only accurate for higher + // aspect ratios => hence the need for machine learning for the actual + // solution) + TacsScalar lam1, lam2; // lam1bar, lam2bar values + nondimShearParams(xi, 0.0, &lam1, &lam2); + TacsScalar dim_factor = M_PI * M_PI * pow(D11 * D22 * D22 * D22, 0.25) / + this->stiffenerPitch / this->stiffenerPitch; + TacsScalar nondim_factor = + (1.0 + pow(lam1, 4.0) + 6.0 * pow(lam1 * lam2, 2.0) + pow(lam2, 4.0) + + 2.0 * xi * (lam1 * lam1 + lam2 * lam2)) / + (2.0 * lam1 * lam1 * lam2); + + // accounts for high and low ARs here + // smooth max of (1,rho_0^{-2}) + TacsScalar shear_geom[2]; + shear_geom[0] = 1.0; + shear_geom[1] = 1.0 / rho_0 / rho_0; + TacsScalar shear_geom_ks = ksAggregation(shear_geom, 2, this->ksWeight); + + return dim_factor * nondim_factor * + shear_geom_ks; // aka N12_crit from CPT closed-form solution + } +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::nondimCriticalLocalShearLoad( + const TacsScalar rho_0, const TacsScalar xi, const TacsScalar zeta) { + if (this->getShearGP()) { + // use Gaussian processes to compute the critical global shear load + TacsScalar s_p = this->stiffenerPitch; + TacsScalar dim_factor = 1.0; + TacsScalar one = 1.0; + XtestShear[0] = log(one + xi); + XtestShear[1] = log(rho_0); + XtestShear[2] = + 0.0; // log(1+gamma) = 0 since gamma=0 for unstiffened panel + XtestShear[3] = log(one + 1000.0 * zeta); + // don't need to use the saved data here since this routine is only meant to + // be called on a single constitutive object (not a whole mesh with O(1e4) + // const objects) + TacsScalar nd_factor = + exp(this->getShearGP()->predictMeanTestData(XtestShear)); + return dim_factor * nd_factor; + + } else { + // use the CPT closed-form solution to compute the critical global axial + // load no mode switching in this solution.. (only accurate for higher + // aspect ratios => hence the need for machine learning for the actual + // solution) + TacsScalar lam1, lam2; // lam1bar, lam2bar values + nondimShearParams(xi, 0.0, &lam1, &lam2); + TacsScalar dim_factor = 1.0; + TacsScalar nondim_factor = + (1.0 + pow(lam1, 4.0) + 6.0 * pow(lam1 * lam2, 2.0) + pow(lam2, 4.0) + + 2.0 * xi * (lam1 * lam1 + lam2 * lam2)) / + (2.0 * lam1 * lam1 * lam2); + + // accounts for high and low ARs here + // smooth max of (1,rho_0^{-2}) + TacsScalar shear_geom[2]; + shear_geom[0] = 1.0; + shear_geom[1] = 1.0 / rho_0 / rho_0; + TacsScalar shear_geom_ks = ksAggregation(shear_geom, 2, this->ksWeight); + nondim_factor *= shear_geom_ks; + + return dim_factor * + nondim_factor; // aka N12_crit from CPT closed-form solution + } +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeCriticalLocalShearLoadSens( + const TacsScalar N12sens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar rho_0, const TacsScalar xi, const TacsScalar zeta, + TacsScalar* D11sens, TacsScalar* D22sens, TacsScalar* spitchsens, + TacsScalar* rho_0sens, TacsScalar* xisens, TacsScalar* zetasens) { + if (this->getShearGP()) { + // use Gaussian processes to compute the critical global shear load + TacsScalar s_p = this->stiffenerPitch; + TacsScalar dim_factor = + M_PI * M_PI * pow(D11 * D22 * D22 * D22, 0.25) / s_p / s_p; + TacsScalar one = 1.0; + XtestShear[0] = log(one + xi); + XtestShear[1] = log(rho_0); + XtestShear[2] = + 0.0; // log(1+gamma) = 0 since gamma=0 for unstiffened panel + XtestShear[3] = log(one + 1000.0 * zeta); + TacsScalar arg = this->panelGPs->predictMeanTestData(3, XtestShear); + TacsScalar nondim_factor = exp(arg); + TacsScalar output = dim_factor * nondim_factor; + + // backwards propagate sensitivities out of the axialGP model to nondim + // params, (this part differentiates the nondim_factor) + TacsScalar Ysens = N12sens * output; + this->panelGPs->predictMeanTestDataSens(3, Ysens, XtestShear, + XtestShearSens); + + *xisens += XtestShearSens[0] / (one + xi); + *rho_0sens += + XtestShearSens[1] / rho_0; // chain rule dlog(rho_0) / drho_0 = 1/rho_0 + *zetasens += XtestShearSens[3] / (one + 1000.0 * zeta) * 1000.0; + + // backpropagate the dimensional factor terms out to the material and + // geometric DVs, (this part is differentiating dim_factor) + *D11sens += Ysens * 0.25 / D11; + *D22sens += Ysens * 0.75 / D22; + *spitchsens += Ysens * -2.0 / s_p; + + // return the final forward analysis output + return output; + + } else { + // use the CPT closed-form solution to compute the critical global axial + // load no mode switching in this solution.. (only accurate for higher + // aspect ratios => hence the need for machine learning for the actual + // solution) + TacsScalar lam1, lam2; // lam1bar, lam2bar values + TacsScalar dl1xi, dl2xi, _dl1gamma, + _dl2gamma; // gamma derivatives are private since unused here (gamma=0 + // input) + + // compute the derivatives of the nondimensional constraints + nondimShearParamsSens(xi, 0.0, &lam1, &lam2, &dl1xi, &_dl1gamma, &dl2xi, + &_dl2gamma); + + // compute forward analysis states involved in the N12crit load + TacsScalar dim_factor = M_PI * M_PI * pow(D11 * D22 * D22 * D22, 0.25) / + this->stiffenerPitch / this->stiffenerPitch; + TacsScalar num = (1.0 + pow(lam1, 4.0) + 6.0 * pow(lam1 * lam2, 2.0) + + pow(lam2, 4.0) + 2.0 * xi * (lam1 * lam1 + lam2 * lam2)); + TacsScalar den = 2.0 * lam1 * lam1 * lam2; + TacsScalar nondim_factor = num / den; + + // accounts for high and low ARs here + // smooth max of (1,rho_0^{-2}) + TacsScalar shear_geom[2]; + shear_geom[0] = 1.0; + shear_geom[1] = 1.0 / rho_0 / rho_0; + TacsScalar shear_geom_ks = ksAggregation(shear_geom, 2, this->ksWeight); + + // final forward output + TacsScalar N12crit = dim_factor * nondim_factor * shear_geom_ks; + + // sensitivities for the non_dim factor + TacsScalar dNDlam1 = + (4.0 * pow(lam1, 3.0) + 12.0 * lam1 * lam2 * lam2 + 4.0 * lam1 * xi) / + den - + num * 4.0 * lam1 * lam2 / den / den; + TacsScalar dNDlam2 = + (4.0 * pow(lam2, 3.0) + 12.0 * lam2 * lam1 * lam1 + 4.0 * lam2 * xi) / + den - + num * 2.0 * lam1 * lam1 / den / den; + + // compute KS aggregation sensitivity + TacsScalar shear_geom_sens[2]; + ksAggregationSens(shear_geom, 2, this->ksWeight, shear_geom_sens); + TacsScalar c_rho0_sens = shear_geom_sens[1] * shear_geom[1] * -2.0 / rho_0; + + // compute the overall sensitivities + *D11sens += N12sens * N12crit * 0.25 / D11; + *D22sens += N12sens * N12crit * 0.75 / D22; + *spitchsens += N12sens * N12crit * -2.0 / this->stiffenerPitch; + *xisens += N12sens * dim_factor * + (dNDlam1 * dl1xi + dNDlam2 * dl2xi + + 2.0 * (lam1 * lam1 + lam2 * lam2) / den) * + shear_geom_ks; + *rho_0sens += N12sens * c_rho0_sens * dim_factor * nondim_factor; + // zetasens unchanged in closed-form + + // return N12crit from closed-form solution + return N12crit; + } +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::computeStiffenerCripplingLoad( + const TacsScalar D11, const TacsScalar D22, const TacsScalar xi, + const TacsScalar rho_0, const TacsScalar genPoiss, const TacsScalar zeta) { + if (this->getCripplingGP()) { + // use Gaussian processes to compute the critical global axial load + TacsScalar dim_factor = M_PI * M_PI * sqrt(D11 * D22) / + this->stiffenerHeight / this->stiffenerHeight; + TacsScalar one = 1.0; + XtestCrippling[0] = log(one + xi); + XtestCrippling[1] = log(rho_0); + XtestCrippling[2] = log(genPoiss); + XtestCrippling[3] = log(one + 1000.0 * zeta); + TacsScalar nd_factor = + exp(this->panelGPs->predictMeanTestData(4, XtestCrippling)); + return dim_factor * nd_factor; + + } else { + // use the literature CPT closed-form solution for approximate stiffener + // crippling, not a function of aspect ratio + TacsScalar dim_factor = M_PI * M_PI * sqrt(D11 * D22) / + this->stiffenerHeight / this->stiffenerHeight; + TacsScalar nondim_factor = (0.476 - 0.56 * (genPoiss - 0.2)) * xi; + return dim_factor * nondim_factor; + } +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::nondimStiffenerCripplingLoad( + const TacsScalar rho_0, const TacsScalar xi, const TacsScalar genPoiss, + const TacsScalar zeta) { + if (this->getCripplingGP()) { + // use Gaussian processes to compute the critical global axial load + TacsScalar dim_factor = 1.0; + TacsScalar one = 1.0; + XtestCrippling[0] = log(one + xi); + XtestCrippling[1] = log(rho_0); + XtestCrippling[2] = log(genPoiss); + XtestCrippling[3] = log(one + 1000.0 * zeta); + // don't need to use the saved data here since this routine is only meant to + // be called on a single constitutive object (not a whole mesh with O(1e4) + // const objects) + TacsScalar nd_factor = + exp(this->getCripplingGP()->predictMeanTestData(XtestCrippling)); + return dim_factor * nd_factor; + + } else { + // use the literature CPT closed-form solution for approximate stiffener + // crippling, not a function of aspect ratio + TacsScalar dim_factor = 1.0; + TacsScalar nondim_factor = (0.476 - 0.56 * (genPoiss - 0.2)) * xi; + return dim_factor * nondim_factor; + } +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::computeStiffenerCripplingLoadSens( + const TacsScalar N1sens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar xi, const TacsScalar rho_0, const TacsScalar genPoiss, + const TacsScalar zeta, TacsScalar* D11sens, TacsScalar* D22sens, + TacsScalar* sheightsens, TacsScalar* xisens, TacsScalar* rho_0sens, + TacsScalar* genPoiss_sens, TacsScalar* zetasens) { + if (this->getCripplingGP()) { + // use Gaussian processes to compute the critical global axial load + TacsScalar dim_factor = M_PI * M_PI * sqrt(D11 * D22) / + this->stiffenerHeight / this->stiffenerHeight; + TacsScalar one = 1.0; + XtestCrippling[0] = log(one + xi); + XtestCrippling[1] = log(rho_0); + XtestCrippling[2] = log(genPoiss); + XtestCrippling[3] = log(one + 1000.0 * zeta); + TacsScalar arg = this->panelGPs->predictMeanTestData(4, XtestCrippling); + TacsScalar nondim_factor = exp(arg); + TacsScalar output = dim_factor * nondim_factor; + + // backwards propagate sensitivities out of the crippling model to nondim + // params, (this part differentiates the nondim_factor) + TacsScalar Ysens = N1sens * output; + this->panelGPs->predictMeanTestDataSens(4, Ysens, XtestCrippling, + XtestCripplingSens); + + *xisens += XtestCripplingSens[0] / (one + xi); + *rho_0sens += XtestCripplingSens[1] / + rho_0; // chain rule dlog(rho_0) / drho_0 = 1/rho_0 + *genPoiss_sens += XtestCripplingSens[2] / genPoiss; + *zetasens += XtestCripplingSens[3] / (one + 1000.0 * zeta) * 1000.0; + + // backpropagate the dimensional factor terms out to the material and + // geometric DVs, (this part is differentiating the dimensional factor) + *D11sens += Ysens * 0.5 / D11; + *D22sens += Ysens * 0.5 / D22; + *sheightsens += Ysens * -2.0 / this->stiffenerHeight; + + // return the final forward analysis output + return output; + + } else { + // use the literature CPT closed-form solution for approximate stiffener + // crippling, not a function of aspect ratio + TacsScalar dim_factor = M_PI * M_PI * sqrt(D11 * D22) / + this->stiffenerHeight / this->stiffenerHeight; + TacsScalar nondim_factor = (0.476 - 0.56 * (genPoiss - 0.2)) * xi; + TacsScalar N11crit = dim_factor * nondim_factor; + + // compute the derivatives + TacsScalar outputsens = N1sens * N11crit; + *D11sens += outputsens * 0.5 / D11; + *D22sens += outputsens * 0.5 / D22; + *xisens += outputsens / xi; + *genPoiss_sens += outputsens / nondim_factor * -0.56 * xi; + *sheightsens += outputsens * -2.0 / this->stiffenerHeight; + // *rho_0sens, *zetasens unchanged here + + // output the critical load here + return N11crit; + } +} + +// ----------------------------------------------------------- +// DERIVATIVE TESTING SUBROUTINES +// ----------------------------------------------------------- +// ----------------------------------------------------------- + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testAffineAspectRatio( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 4; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = 10.341; // D11 + x0[1] = 5.216; // D22 + x0[2] = 3.124; // a + x0[3] = 1.061; // b + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + f0 = computeAffineAspectRatio(x[0], x[1], x[2], x[3]); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + f2 = computeAffineAspectRatio(x[0], x[1], x[2], x[3]); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + computeAffineAspectRatioSens(p_output, x0[0], x0[1], x0[2], x0[3], + &input_sens[0], &input_sens[1], &input_sens[2], + &input_sens[3]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testAffineAspectRatio:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testLaminateIsotropy( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 4; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = 10.341; // D11 + x0[1] = 5.216; // D22 + x0[2] = 6.132; // D12 + x0[3] = 2.103; // D66 + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + f0 = computeLaminateIsotropy(x[0], x[1], x[2], x[3]); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + f2 = computeLaminateIsotropy(x[0], x[1], x[2], x[3]); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + computeLaminateIsotropySens(p_output, x0[0], x0[1], x0[2], x0[3], + &input_sens[0], &input_sens[1], &input_sens[2], + &input_sens[3]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testLaminateIsotropy:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testGeneralizedPoissonsRatio( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 2; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = 10.341; // D12 + x0[1] = 5.381; // D66 + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + f0 = computeGeneralizedPoissonsRatio(x[0], x[1]); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + f2 = computeGeneralizedPoissonsRatio(x[0], x[1]); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + computeGeneralizedPoissonsRatioSens(p_output, x0[0], x0[1], &input_sens[0], + &input_sens[1]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testGeneralizedPoissonsRatio:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testStiffenerAreaRatio( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 6; + TacsScalar p_input[n_input], x0[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = this->stiffenerThick; + x0[1] = this->stiffenerHeight; + x0[2] = this->stiffenerPitch; + x0[3] = this->panelThick; + x0[4] = 1.145e8; // E11p + x0[5] = 2.17e7; // E11s + + TacsScalar E1p, E1s; + E1p = x0[4] * 1.0; + E1s = x0[5] * 1.0; + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + this->stiffenerThick -= p_input[0] * epsilon; + this->stiffenerHeight -= p_input[1] * epsilon; + this->stiffenerPitch -= p_input[2] * epsilon; + this->panelThick -= p_input[3] * epsilon; + E1p -= p_input[4] * epsilon; + E1s -= p_input[5] * epsilon; + f0 = computeStiffenerAreaRatio(E1p, E1s); + + this->stiffenerThick += 2.0 * p_input[0] * epsilon; + this->stiffenerHeight += 2.0 * p_input[1] * epsilon; + this->stiffenerPitch += 2.0 * p_input[2] * epsilon; + this->panelThick += 2.0 * p_input[3] * epsilon; + E1p += 2.0 * p_input[4] * epsilon; + E1s += 2.0 * p_input[5] * epsilon; + f2 = computeStiffenerAreaRatio(E1p, E1s); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // reset the values + this->stiffenerThick -= p_input[0] * epsilon; + this->stiffenerHeight -= p_input[1] * epsilon; + this->stiffenerPitch -= p_input[2] * epsilon; + this->panelThick -= p_input[3] * epsilon; + E1p -= p_input[4] * epsilon; + E1s -= p_input[5] * epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + computeStiffenerAreaRatioSens(p_output, E1p, E1s, &input_sens[0], + &input_sens[1], &input_sens[2], &input_sens[3], + &input_sens[4], &input_sens[5]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testStiffenerAreaRatio:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testStiffenerStiffnessRatio( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 6; + TacsScalar p_input[n_input], x0[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = 10.2143; // D11 + x0[1] = this->stiffenerThick; + x0[2] = this->stiffenerHeight; + x0[3] = this->stiffenerPitch; + x0[4] = 1.145e8; // E1s + x0[5] = this->stiffenerHeight * 0.143; // zn overall centroid + + TacsScalar E1s = x0[4] * 1.0; + TacsScalar zn = x0[5] * 1.0; + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + TacsScalar D11 = x0[0] * 1.0; + D11 -= p_input[0] * epsilon; + this->stiffenerThick -= p_input[1] * epsilon; + this->stiffenerHeight -= p_input[2] * epsilon; + this->stiffenerPitch -= p_input[3] * epsilon; + E1s -= p_input[4] * epsilon; + zn -= p_input[5] * epsilon; + f0 = computeStiffenerStiffnessRatio(D11, E1s, zn); + + D11 += 2.0 * p_input[0] * epsilon; + this->stiffenerThick += 2.0 * p_input[1] * epsilon; + this->stiffenerHeight += 2.0 * p_input[2] * epsilon; + this->stiffenerPitch += 2.0 * p_input[3] * epsilon; + E1s += 2.0 * p_input[4] * epsilon; + zn += 2.0 * p_input[5] * epsilon; + f2 = computeStiffenerStiffnessRatio(D11, E1s, zn); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // reset the values + D11 -= p_input[0] * epsilon; + this->stiffenerThick -= p_input[1] * epsilon; + this->stiffenerHeight -= p_input[2] * epsilon; + this->stiffenerPitch -= p_input[3] * epsilon; + E1s -= p_input[4] * epsilon; + zn -= p_input[5] * epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + computeStiffenerStiffnessRatioSens( + p_output, D11, E1s, zn, &input_sens[0], &input_sens[1], &input_sens[2], + &input_sens[3], &input_sens[4], &input_sens[5]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testStiffenerStiffnessRatio:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testTransverseShearParameter( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 4; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = 100.234; // A66 + x0[1] = 421.341; // A11 + x0[2] = 2.134; // b + x0[3] = 0.0112; // h + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + f0 = computeTransverseShearParameter(x[0], x[1], x[2], x[3]); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + f2 = computeTransverseShearParameter(x[0], x[1], x[2], x[3]); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + computeTransverseShearParameterSens(p_output, x0[0], x0[1], x0[2], x0[3], + &input_sens[0], &input_sens[1], + &input_sens[2], &input_sens[3]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testTransverseShearParameter:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testNondimensionalParameters( + TacsScalar epsilon, int printLevel) { + // run each of the nondim parameter tests and aggregate the max among them + const int n_tests = 6; + TacsScalar relErrors[n_tests]; + + if (printLevel != 0) { + printf("\nTACSGPBladeStiffened..testNondimensionalParmeters start::\n"); + printf("--------------------------------------------------------\n\n"); + } + + relErrors[0] = testAffineAspectRatio(epsilon, printLevel); + relErrors[1] = testLaminateIsotropy(epsilon, printLevel); + relErrors[2] = testGeneralizedPoissonsRatio(epsilon, printLevel); + relErrors[3] = testStiffenerAreaRatio(epsilon, printLevel); + relErrors[4] = testStiffenerStiffnessRatio(epsilon, printLevel); + relErrors[5] = testTransverseShearParameter(epsilon, printLevel); + + // get max rel error among them + TacsScalar maxRelError = 0.0; + for (int i = 0; i < n_tests; i++) { + if (TacsRealPart(relErrors[i]) > TacsRealPart(maxRelError)) { + maxRelError = relErrors[i]; + } + } + + // report the overall test results + if (printLevel != 0) { + printf( + "\nTACSGPBladeStiffened..testNondimensionalParmeters final " + "results::\n"); + printf("\ttestAffineAspectRatio = %.4e\n", TacsRealPart(relErrors[0])); + printf("\ttestLaminateIsotropy = %.4e\n", TacsRealPart(relErrors[1])); + printf("\ttestGeneralizedPoissonsRatio = %.4e\n", + TacsRealPart(relErrors[2])); + printf("\ttestStiffenerAreaRatio = %.4e\n", TacsRealPart(relErrors[3])); + printf("\ttestStiffenerStiffnessRatio = %.4e\n", + TacsRealPart(relErrors[4])); + printf("\ttestTransverseShearParameter = %.4e\n", + TacsRealPart(relErrors[5])); + printf("\tOverall max rel error = %.4e\n\n", TacsRealPart(maxRelError)); + } + + return maxRelError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testAxialCriticalLoads( + TacsScalar epsilon, int printLevel) { + // run each of the nondim parameter tests and aggregate the max among them + const int n_tests = 2; + TacsScalar relErrors[n_tests]; + + if (printLevel != 0) { + printf("\nTACSGPBladeStiffened..testAxialCriticalLoads start::\n"); + printf("--------------------------------------------------------\n\n"); + } + + relErrors[0] = testCriticalGlobalAxialLoad(epsilon, printLevel); + relErrors[1] = testCriticalLocalAxialLoad(epsilon, printLevel); + + // get max rel error among them + TacsScalar maxRelError = 0.0; + for (int i = 0; i < n_tests; i++) { + if (TacsRealPart(relErrors[i]) > TacsRealPart(maxRelError)) { + maxRelError = relErrors[i]; + } + } + + // get max rel error among them + if (printLevel != 0) { + printf("\nTACSGPBladeStiffened..testAxialCriticalLoads final results::\n"); + printf("\ttestGlobalAxialLoad = %.4e\n", TacsRealPart(relErrors[0])); + printf("\ttestLocalAxialLoad = %.4e\n", TacsRealPart(relErrors[1])); + printf("\tOverall max rel error = %.4e\n\n", TacsRealPart(maxRelError)); + } + + return maxRelError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testCriticalGlobalAxialLoad( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 8; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = 10.2412; // D11 + x0[1] = 5.4323; // D22 + x0[2] = 2.134; // b + x0[3] = 0.13432; // delta + x0[4] = 2.4545; // rho0 + x0[5] = 1.24332; // xi + x0[6] = 0.2454; // gamma + x0[7] = 1e-3; // zeta + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + this->panelGPs->resetSavedData(); + f0 = computeCriticalGlobalAxialLoad(x[0], x[1], x[2], x[3], x[4], x[5], x[6], + x[7]); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + this->panelGPs->resetSavedData(); + f2 = computeCriticalGlobalAxialLoad(x[0], x[1], x[2], x[3], x[4], x[5], x[6], + x[7]); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + for (int i = 0; i < n_input; i++) { + x[i] = x0[i]; + } + this->panelGPs->resetSavedData(); + computeCriticalGlobalAxialLoadSens( + p_output, x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], &input_sens[0], + &input_sens[1], &input_sens[2], &input_sens[3], &input_sens[4], + &input_sens[5], &input_sens[6], &input_sens[7]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testCriticalGlobalAxialLoad:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testCriticalLocalAxialLoad( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 6; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = 10.2412; // D11 + x0[1] = 5.4323; // D22 + x0[2] = this->stiffenerPitch; // s_p + x0[3] = 2.4545; // rho0 + x0[4] = 1.24332; // xi + x0[5] = 1e-3; // zeta + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + this->stiffenerPitch = x0[2] - p_input[2] * epsilon; + this->panelGPs->resetSavedData(); + f0 = computeCriticalLocalAxialLoad(x[0], x[1], x[3], x[4], x[5]); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + this->stiffenerPitch = x0[2] + p_input[2] * epsilon; + this->panelGPs->resetSavedData(); + f2 = computeCriticalLocalAxialLoad(x[0], x[1], x[3], x[4], x[5]); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + this->stiffenerPitch = x0[2]; + for (int i = 0; i < n_input; i++) { + x[i] = x0[i]; + } + this->panelGPs->resetSavedData(); + computeCriticalLocalAxialLoadSens( + p_output, x[0], x[1], x[3], x[4], x[5], &input_sens[0], &input_sens[1], + &input_sens[2], &input_sens[3], &input_sens[4], &input_sens[5]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testCriticalLocalAxialLoad:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testOverallCentroid( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 6; + TacsScalar p_input[n_input], x0[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = this->stiffenerThick; + x0[1] = this->stiffenerHeight; + x0[2] = this->panelThick; + x0[3] = this->stiffenerPitch; + x0[4] = 1.734e5; // E11p + x0[5] = 8.213e4; // E11s + TacsScalar E1p = x0[4] * 1.0; + TacsScalar E1s = x0[5] * 1.0; + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + this->stiffenerThick -= p_input[0] * epsilon; + this->stiffenerHeight -= p_input[1] * epsilon; + this->panelThick -= p_input[2] * epsilon; + this->stiffenerPitch -= p_input[3] * epsilon; + E1p -= p_input[4] * epsilon; + E1s -= p_input[5] * epsilon; + f0 = computeOverallCentroid(E1p, E1s); + + this->stiffenerThick += 2.0 * p_input[0] * epsilon; + this->stiffenerHeight += 2.0 * p_input[1] * epsilon; + this->panelThick += 2.0 * p_input[2] * epsilon; + this->stiffenerPitch += 2.0 * p_input[3] * epsilon; + E1p += 2.0 * p_input[4] * epsilon; + E1s += 2.0 * p_input[5] * epsilon; + f2 = computeOverallCentroid(E1p, E1s); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // reset the values + this->stiffenerThick -= p_input[0] * epsilon; + this->stiffenerHeight -= p_input[1] * epsilon; + this->panelThick -= p_input[2] * epsilon; + this->stiffenerPitch -= p_input[3] * epsilon; + E1p -= p_input[4] * epsilon; + E1s -= p_input[5] * epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + computeOverallCentroidSens(p_output, E1p, E1s, &input_sens[0], &input_sens[1], + &input_sens[2], &input_sens[3], &input_sens[4], + &input_sens[5]); + + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testOverallCentroid:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::testPanelGlobalBucklingStiffness( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 4; + TacsScalar p_input[n_input], x0[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = this->stiffenerPitch; + x0[1] = this->panelThick; + x0[2] = 1.734e2; // E11p (lower value to make test easier) + x0[3] = 0.0413; // zn + TacsScalar E1p = x0[2] * 1.0; + TacsScalar zn = x0[3] * 1.0; + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + this->stiffenerPitch -= p_input[0] * epsilon; + this->panelThick -= p_input[1] * epsilon; + E1p -= p_input[2] * epsilon; + zn -= p_input[3] * epsilon; + computePanelGlobalBucklingStiffness(E1p, zn, &f0); + + this->stiffenerPitch += 2.0 * p_input[0] * epsilon; + this->panelThick += 2.0 * p_input[1] * epsilon; + E1p += 2.0 * p_input[2] * epsilon; + zn += 2.0 * p_input[3] * epsilon; + computePanelGlobalBucklingStiffness(E1p, zn, &f2); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // reset the values + this->stiffenerPitch -= p_input[0] * epsilon; + this->panelThick -= p_input[1] * epsilon; + E1p -= p_input[2] * epsilon; + zn -= p_input[3] * epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + computePanelGlobalBucklingStiffnessSens(p_output, E1p, zn, &input_sens[0], + &input_sens[1], &input_sens[2], + &input_sens[3]); + + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testPanelGlobalBucklingStiffness:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testOtherTests( + TacsScalar epsilon, int printLevel) { + // run each of the nondim parameter tests and aggregate the max among them + const int n_tests = 2; + TacsScalar relErrors[n_tests]; + + if (printLevel != 0) { + printf("\nTACSGPBladeStiffened..testOtherTests start::\n"); + printf("--------------------------------------------------------\n\n"); + } + + // first two tests are used for the closed-form solution approach + relErrors[0] = testOverallCentroid(epsilon, printLevel); + relErrors[1] = testPanelGlobalBucklingStiffness(epsilon, printLevel); + + // get max rel error among them + TacsScalar maxRelError = 0.0; + for (int i = 0; i < n_tests; i++) { + if (TacsRealPart(relErrors[i]) > TacsRealPart(maxRelError)) { + maxRelError = relErrors[i]; + } + } + + // get max rel error among them + if (printLevel != 0) { + printf("\nTACSGPBladeStiffened..testOtherTests final results::\n"); + printf("\ttestOverallCentroid = %.4e\n", TacsRealPart(relErrors[0])); + printf("\ttestPanelGlobalBucklingStiffness = %.4e\n", + TacsRealPart(relErrors[1])); + printf("\tOverall max rel error = %.4e\n\n", TacsRealPart(maxRelError)); + } + + return maxRelError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testShearCriticalLoads( + TacsScalar epsilon, int printLevel) { + // run each of the nondim parameter tests and aggregate the max among them + const int n_tests = 6; + TacsScalar relErrors[n_tests]; + + if (printLevel != 0) { + printf("\nTACSGPBladeStiffened..testShearCriticalLoads start::\n"); + printf("--------------------------------------------------------\n\n"); + } + + // first two tests are used for the closed-form solution approach + relErrors[0] = testLam2Constraint(epsilon, printLevel); + relErrors[1] = testNondimShearParams(epsilon, printLevel); + // final crit load tests + relErrors[2] = testCriticalGlobalShearLoad(epsilon, printLevel); + relErrors[3] = testCriticalLocalShearLoad(epsilon, printLevel); + relErrors[4] = testCriticalGlobalShearLoad_LowAR(epsilon, printLevel); + relErrors[5] = testCriticalLocalShearLoad_LowAR(epsilon, printLevel); + + // get max rel error among them + TacsScalar maxRelError = 0.0; + for (int i = 0; i < n_tests; i++) { + if (TacsRealPart(relErrors[i]) > TacsRealPart(maxRelError)) { + maxRelError = relErrors[i]; + } + } + + // get max rel error among them + if (printLevel != 0) { + printf("\nTACSGPBladeStiffened..testShearCriticalLoads final results::\n"); + printf("\ttestLam2Constraint = %.4e\n", TacsRealPart(relErrors[0])); + printf("\ttestNondimShearParams = %.4e\n", TacsRealPart(relErrors[1])); + printf("\ttestGlobalShearLoad = %.4e\n", TacsRealPart(relErrors[2])); + printf("\ttestLocalShearLoad = %.4e\n", TacsRealPart(relErrors[3])); + printf("\ttestGlobalShearLoad_LowAR = %.4e\n", TacsRealPart(relErrors[4])); + printf("\ttestLocalShearLoad_LowAR = %.4e\n", TacsRealPart(relErrors[5])); + printf("\tOverall max rel error = %.4e\n\n", TacsRealPart(maxRelError)); + } + + return maxRelError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testLam2Constraint( + const TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 1; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = 0.78153; // lam2Sq + // x0[1] = 1.24332; // xi + // x0[2] = 0.2454; //gamma + TacsScalar xi = 1.24332; // xi + TacsScalar gamma = 0.2454; // gamma + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + f0 = lam2Constraint(x[0], xi, gamma); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + f2 = lam2Constraint(x[0], xi, gamma); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + for (int i = 0; i < n_input; i++) { + x[i] = x0[i]; + } + TacsScalar lam2Deriv = lam2ConstraintDeriv(x[0], xi, gamma); + TacsScalar adjTD = p_output * lam2Deriv * p_input[0]; + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testLam2Constraint:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testNondimShearParams( + const TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 2; + TacsScalar p_input[n_input], x0[n_input], x[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + // temporarily fix gamma perturbation + // p_input[1] = 0.0; + + const int n_output = 2; + TacsScalar p_output[n_output], f0[n_output], f2[n_output]; + for (int ii = 0; ii < n_output; ii++) { + p_output[ii] = ((double)rand() / (RAND_MAX)); + } + + // compute initial values + x0[0] = 1.24332; // xi + x0[1] = 0.2454; // gamma + + // perform central difference over rho_0 function on [D11,D22,a,b] + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + nondimShearParams(x[0], x[1], &f0[0], &f0[1]); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + nondimShearParams(x[0], x[1], &f2[0], &f2[1]); + + TacsScalar centralDiff = 0.0; + for (int ii = 0; ii < n_output; ii++) { + centralDiff += p_output[ii] * (f2[ii] - f0[ii]) / 2.0 / epsilon; + } + + // now perform the adjoint sensitivity + TacsScalar _lam[n_input], l1sens[n_input], l2sens[n_input]; + memset(l1sens, 0, n_input * sizeof(TacsScalar)); + memset(l2sens, 0, n_input * sizeof(TacsScalar)); + for (int i = 0; i < n_input; i++) { + x[i] = x0[i]; + } + nondimShearParamsSens(x[0], x[1], &_lam[0], &_lam[1], &l1sens[0], &l1sens[1], + &l2sens[0], &l2sens[1]); + TacsScalar adjTD = 0.0; + for (int i = 0; i < n_input; i++) { + adjTD += p_output[0] * l1sens[i] * p_input[i]; + adjTD += p_output[1] * l2sens[i] * p_input[i]; + } + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testNondimShearParams:\n"); + printf("\t\tadjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\tcentralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\trel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testCriticalGlobalShearLoad( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 7; + + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = 10.2412; // D11 + x0[1] = 5.4323; // D22 + x0[2] = 2.134; // b + x0[3] = 2.4545; // rho0 + x0[4] = 1.24332; // xi + x0[5] = 0.2454; // gamma + x0[6] = 1e-3; // zeta + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + this->panelGPs->resetSavedData(); + f0 = computeCriticalGlobalShearLoad(x[0], x[1], x[2], x[3], x[4], x[5], x[6]); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + this->panelGPs->resetSavedData(); + f2 = computeCriticalGlobalShearLoad(x[0], x[1], x[2], x[3], x[4], x[5], x[6]); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + for (int i = 0; i < n_input; i++) { + x[i] = x0[i]; + } + this->panelGPs->resetSavedData(); + computeCriticalGlobalShearLoadSens( + p_output, x[0], x[1], x[2], x[3], x[4], x[5], x[6], &input_sens[0], + &input_sens[1], &input_sens[2], &input_sens[3], &input_sens[4], + &input_sens[5], &input_sens[6]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testCriticalGlobalShearLoad:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::testCriticalGlobalShearLoad_LowAR( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 7; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = 10.2412; // D11 + x0[1] = 5.4323; // D22 + x0[2] = 2.134; // b + x0[3] = 0.2543; // rho0 + x0[4] = 1.24332; // xi + x0[5] = 0.2454; // gamma + x0[6] = 1e-3; // zeta + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + this->panelGPs->resetSavedData(); + f0 = computeCriticalGlobalShearLoad(x[0], x[1], x[2], x[3], x[4], x[5], x[6]); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + this->panelGPs->resetSavedData(); + f2 = computeCriticalGlobalShearLoad(x[0], x[1], x[2], x[3], x[4], x[5], x[6]); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + for (int i = 0; i < n_input; i++) { + x[i] = x0[i]; + } + this->panelGPs->resetSavedData(); + computeCriticalGlobalShearLoadSens( + p_output, x[0], x[1], x[2], x[3], x[4], x[5], x[6], &input_sens[0], + &input_sens[1], &input_sens[2], &input_sens[3], &input_sens[4], + &input_sens[5], &input_sens[6]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testCriticalGlobalShearLoad:_LowAR\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testCriticalLocalShearLoad( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 6; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = 10.2412; // D11 + x0[1] = 5.4323; // D22 + x0[2] = this->stiffenerPitch; // s_p + x0[3] = 1.24332; // xi + x0[4] = 2.4545; // rho0 + x0[5] = 1e-3; // zeta + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + this->stiffenerPitch = x0[2] - p_input[2] * epsilon; + this->panelGPs->resetSavedData(); + f0 = computeCriticalLocalShearLoad(x[0], x[1], x[3], x[4], x[5]); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + this->panelGPs->resetSavedData(); + this->stiffenerPitch = x0[2] + p_input[2] * epsilon; + f2 = computeCriticalLocalShearLoad(x[0], x[1], x[3], x[4], x[5]); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + this->stiffenerPitch = x0[2]; + for (int i = 0; i < n_input; i++) { + x[i] = x0[i]; + } + this->panelGPs->resetSavedData(); + computeCriticalLocalShearLoadSens( + p_output, x[0], x[1], x[3], x[4], x[5], &input_sens[0], &input_sens[1], + &input_sens[2], &input_sens[3], &input_sens[4], &input_sens[5]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testCriticalLocalShearLoad:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar +TACSGPBladeStiffenedShellConstitutive::testCriticalLocalShearLoad_LowAR( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 6; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + x0[0] = 10.2412; // D11 + x0[1] = 5.4323; // D22 + x0[2] = this->stiffenerPitch; // s_p + x0[3] = 1.24332; // xi + x0[4] = 0.34251; // rho0 + x0[5] = 1e-3; // zeta + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + this->stiffenerPitch = x0[2] - p_input[2] * epsilon; + this->panelGPs->resetSavedData(); + f0 = computeCriticalLocalShearLoad(x[0], x[1], x[3], x[4], x[5]); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + this->panelGPs->resetSavedData(); + this->stiffenerPitch = x0[2] + p_input[2] * epsilon; + f2 = computeCriticalLocalShearLoad(x[0], x[1], x[3], x[4], x[5]); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + this->stiffenerPitch = x0[2]; + for (int i = 0; i < n_input; i++) { + x[i] = x0[i]; + } + this->panelGPs->resetSavedData(); + computeCriticalLocalShearLoadSens( + p_output, x[0], x[1], x[3], x[4], x[5], &input_sens[0], &input_sens[1], + &input_sens[2], &input_sens[3], &input_sens[4], &input_sens[5]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\tTACSGPBladeStiffened..testCriticalLocalShearLoad_LowAR:\n"); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testStiffenerCripplingLoad( + TacsScalar epsilon, int printLevel) { + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 7; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + if (printLevel != 0) { + printf("\nTACSGPBladeStiffened..testStiffenerCripplingLoad start::\n"); + printf("--------------------------------------------------------\n\n"); + } + + // compute initial values + x0[0] = 10.2412; // D11 + x0[1] = 5.4323; // D22 + x0[2] = this->stiffenerHeight; // sheight + x0[3] = 1.24332; // xi + x0[4] = 2.4545; // rho0 + x0[5] = 0.2454; // genPoiss + x0[6] = 1e-3; // zeta + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + this->stiffenerHeight = x0[2] - p_input[2] * epsilon; + this->panelGPs->resetSavedData(); + f0 = computeStiffenerCripplingLoad(x[0], x[1], x[3], x[4], x[5], x[6]); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + this->stiffenerHeight = x0[2] + p_input[2] * epsilon; + this->panelGPs->resetSavedData(); + f2 = computeStiffenerCripplingLoad(x[0], x[1], x[3], x[4], x[5], x[6]); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + this->stiffenerHeight = x0[2]; + for (int i = 0; i < n_input; i++) { + x[i] = x0[i]; + } + this->panelGPs->resetSavedData(); + computeStiffenerCripplingLoadSens( + p_output, x[0], x[1], x[3], x[4], x[5], x[6], &input_sens[0], + &input_sens[1], &input_sens[2], &input_sens[3], &input_sens[4], + &input_sens[5], &input_sens[6]); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("TACSGPBladeStiffened..testStiffenerCripplingLoad:\n"); + printf("\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSGPBladeStiffenedShellConstitutive::testAllTests( + TacsScalar epsilon, int printLevel) { + // run each of the nondim parameter tests and aggregate the max among them + const int n_tests = 8; + TacsScalar relErrors[n_tests]; + memset(relErrors, 0, n_tests * sizeof(TacsScalar)); + + if (printLevel != 0) { + printf("\nTACSGPBladeStiffened..testAllTests start::\n"); + printf("========================================================\n"); + printf("========================================================\n\n"); + } + + relErrors[0] = testNondimensionalParameters(epsilon, printLevel); + relErrors[1] = testAxialCriticalLoads(epsilon, printLevel); + relErrors[2] = testShearCriticalLoads(epsilon, printLevel); + relErrors[3] = testStiffenerCripplingLoad(epsilon, printLevel); + relErrors[7] = testOtherTests(epsilon, printLevel); + + if (this->getAxialGP()) { + if (printLevel != 0) { + printf("\nAxialGP : testAllGPtests start::\n"); + printf("--------------------------------------------------------\n\n"); + } + relErrors[4] = this->getAxialGP()->testAllGPTests(epsilon, printLevel); + } + if (this->getShearGP()) { + if (printLevel != 0) { + printf("\nShearGP : testAllGPtests start::\n"); + printf("--------------------------------------------------------\n\n"); + } + relErrors[5] = this->getShearGP()->testAllGPTests(epsilon, printLevel); + } + if (this->getCripplingGP()) { + if (printLevel != 0) { + printf("\nCripplingGP : testAllGPtests start::\n"); + printf("--------------------------------------------------------\n\n"); + } + relErrors[6] = this->getCripplingGP()->testAllGPTests(epsilon, printLevel); + } + + // get max rel error among them + TacsScalar maxRelError = 0.0; + for (int i = 0; i < n_tests; i++) { + if (TacsRealPart(relErrors[i]) > TacsRealPart(maxRelError)) { + maxRelError = relErrors[i]; + } + } + + // get max rel error among them + if (printLevel != 0) { + printf("========================================================\n"); + printf("\nTACSGPBladeStiffened..testAllTests full results::\n"); + printf("\ttestNondimensionalParameters = %.4e\n", + TacsRealPart(relErrors[0])); + printf("\ttestAxialCriticalLoads = %.4e\n", TacsRealPart(relErrors[1])); + printf("\ttestShearCriticalLoads = %.4e\n", TacsRealPart(relErrors[2])); + printf("\ttestStiffenerCripplingLoad = %.4e\n", TacsRealPart(relErrors[3])); + if (this->getAxialGP()) { + printf("\ttestAxialGP all tests = %.4e\n", TacsRealPart(relErrors[4])); + } + if (this->getShearGP()) { + printf("\ttestShearGP all tests = %.4e\n", TacsRealPart(relErrors[5])); + } + if (this->getCripplingGP()) { + printf("\ttestCripplingGp all tests = %.4e\n", + TacsRealPart(relErrors[6])); + } + printf("\ttestOtherTests = %.4e\n", TacsRealPart(relErrors[7])); + printf("\tOverall max rel error = %.4e\n\n", TacsRealPart(maxRelError)); + } + + return maxRelError; +} + +void TACSGPBladeStiffenedShellConstitutive::setKSWeight(double ksWeight) { + // call the superclass method to set the ksWeight for this const class + TACSBladeStiffenedShellConstitutive::setKSWeight(ksWeight); +} diff --git a/src/constitutive/TACSGPBladeStiffenedShellConstitutive.h b/src/constitutive/TACSGPBladeStiffenedShellConstitutive.h new file mode 100644 index 000000000..d5880d599 --- /dev/null +++ b/src/constitutive/TACSGPBladeStiffenedShellConstitutive.h @@ -0,0 +1,1241 @@ +/* +==================================================================================== +Blade-Stiffened Shell Constitutive Model using Machine Learning Buckling +Constraints +==================================================================================== +@File : TACSGPBladeStiffenedShellConstutive.h +@Date : 2024/04/24 +@Author : Sean Phillip Engelstad +@Description : Constitutive model for a blade-stiffened shell. Based on the FSDT +blade models adopted by Alasdair Christian Gray in the +TACSBladeStiffenedShellConstutitutive.h class and the original one by Graeme +Kennedy. Gaussian Processes for Machine Learning are used for the buckling +constraints of the stiffened panels. +*/ + +#pragma once + +// ============================================================================= +// Standard Library Includes +// ============================================================================= + +// ============================================================================= +// Extension Includes +// ============================================================================= +#include "TACSBladeStiffenedShellConstitutive.h" +#include "TACSGaussianProcessModel.h" +#include "TACSPanelGPs.h" +#include "TacsUtilities.h" + +// ============================================================================= +// Class Declaration +// ============================================================================= + +/** + * @brief Constitutive model for a blade-stiffened shell + * this class inherits from TACSBladeStiffenedShellConstitutive.cpp, and + * overwrites some buckling predictions using machine learning or more accurate + * closed-form solutions for global and local buckling. + * + * The TACSPanelGPs object is input into this class for each panel (see + * smdogroup/ml_buckling repo on examples from the python side), which is useful + * in saving and reloading buckling predictions for each element in the same + * panel / TACS component (to speedup computation). To use Machine Learning + * buckling predictions with a Gaussian Process model for global and local + * buckling, the user should create TACSAxialGaussianProcessModel and + * TACSShearGaussianProcessModel objects in the TACSPanelGPs. If the + * TACSAxialGaussianProcessModel or TACSShearGaussianProcessModel objects are + * empty or the TACSPanelGPs is empty, then the axial or shear buckling loads + * will use closed-form solution buckling predictions instead. Note that either + * the Axial GP or the shear GP can each be provided or not. Data for the + * trained GP models is located on the smdogroup/ml_buckling github repo. + * + * The main differences in this class and its superclass + * TACSBladeStiffenedShellConstitutive.cpp for the constructor are the new + * inputs for panelWidth and panelWidthNum, an additional flag for + * CPTstiffenerCrippling, and the PanelGPs object which holds machine learning + * objects. + * + */ +class TACSGPBladeStiffenedShellConstitutive + : public TACSBladeStiffenedShellConstitutive { + public: + /** + * @brief Construct a new TACSBladeStiffenedShellConstitutive object + * + * @param panelPly Orthotropic ply object for the panel + * @param stiffenerPly Orthotropic ply object for the stiffener + * @param kcorr Shear correction factor + * @param panelLength Panel length value + * @param panelLengthNum Panel Length design variable number + * @param stiffenerPitch Stiffener pitch value + * @param stiffenerPitchNum Stiffener pitch design variable number + * @param panelThick Panel thickness value + * @param panelThickNum Panel thickness design variable number + * @param numPanelPlies Number of ply angles in the panel laminate + * @param panelPlyAngles Panel ply angles + * @param panelPlyFracs Panel ply fractions + * @param panelPlyFracNums Panel ply fraction design variable numbers + * @param stiffenerHeight Stiffener height value + * @param stiffenerHeightNum Stiffener height design variable number + * @param stiffenerThick Stiffener thickness value + * @param stiffenerThickNum Stiffener thickness design variable number + * @param numStiffenerPlies Number of ply angles in the stiffener laminate + * @param stiffenerPlyAngles Stiffener ply angles + * @param stiffenerPlyFracs Stiffener ply fractions + * @param stiffenerPlyFracNums Stiffener ply fraction design variable numbers + * @param panelWidth Panel Width value + * @param panelWidthNum Panel Width design variable number + * @param flangeFraction Stiffener base width as a fraction of the stiffener + * @param CPTstiffenerCrippling whether to use CPT solution (true) or to use + * experimental crippling solution (false) + * @param panelGPs PanelGP object (one for each TACS component) that contains + * the GP objects or None if using CF + */ + TACSGPBladeStiffenedShellConstitutive( + TACSOrthotropicPly* panelPly, TACSOrthotropicPly* stiffenerPly, + TacsScalar kcorr, TacsScalar panelLength, int panelLengthNum, + TacsScalar stiffenerPitch, int stiffenerPitchNum, TacsScalar panelThick, + int panelThickNum, int numPanelPlies, TacsScalar panelPlyAngles[], + TacsScalar panelPlyFracs[], int panelPlyFracNums[], + TacsScalar stiffenerHeight, int stiffenerHeightNum, + TacsScalar stiffenerThick, int stiffenerThickNum, int numStiffenerPlies, + TacsScalar stiffenerPlyAngles[], TacsScalar stiffenerPlyFracs[], + int stiffenerPlyFracNums[], TacsScalar panelWidth, int panelWidthNum, + TacsScalar flangeFraction = 1.0, bool CPTstiffenerCrippling = false, + TACSPanelGPs* panelGPs = nullptr); + + ~TACSGPBladeStiffenedShellConstitutive(); + + // ============================================================================== + // Tests + // ============================================================================== + + /** + * + * @brief Test the nondimensional parameter computations against finite + * difference. Tests all non-dimensional parameter subtests inside this one + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error among all non-dimensional parameter + * subtests + */ + TacsScalar testNondimensionalParameters(TacsScalar epsilon, int printLevel); + + /** + * + * @brief Test the axial critical loads => includes global axial and local + * axial buckling subtests + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of global axial and local axial subtests + */ + TacsScalar testAxialCriticalLoads(TacsScalar epsilon, int printLevel); + + /** + * + * @brief Test the shear critical loads => includes local shear and global + * shear subtests + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testShearCriticalLoads(TacsScalar epsilon, int printLevel); + + /** + * + * @brief Test the crippling critical loads + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testStiffenerCripplingLoad(TacsScalar epsilon, int printLevel); + + /** + * + * @brief Test all GP tests + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error among all the subtest derivative tests + */ + TacsScalar testAllTests(TacsScalar epsilon, int printLevel); + + // ============================================================================== + // Getter and setters + // ============================================================================== + + // get the three Gaussian Process model pointers + TACSBucklingGaussianProcessModel* getAxialGP() { + if (this->panelGPs) { + return this->panelGPs->getAxialGP(); + } else { + return nullptr; + } + } + TACSBucklingGaussianProcessModel* getShearGP() { + if (this->panelGPs) { + return this->panelGPs->getShearGP(); + } else { + return nullptr; + } + } + TACSBucklingGaussianProcessModel* getCripplingGP() { + if (this->panelGPs) { + return this->panelGPs->getCripplingGP(); + } else { + return nullptr; + } + } + + // Retrieve the global design variable numbers + int getDesignVarNums(int elemIndex, int dvLen, int dvNums[]); + + // Set the element design variable from the design vector + int setDesignVars(int elemIndex, int dvLen, const TacsScalar dvs[]); + + // Get the element design variables values + int getDesignVars(int elemIndex, int dvLen, TacsScalar dvs[]); + + // Get the lower and upper bounds for the design variable values + int getDesignVarRange(int elemIndex, int dvLen, TacsScalar lb[], + TacsScalar ub[]); + + // Retrieve the design variable for plotting purposes + TacsScalar evalDesignFieldValue(int elemIndex, const double pt[], + const TacsScalar X[], int index); + + // set the KS weight for the failure constraints and the GP models (if GP + // models are active) + void setKSWeight(double ksWeight); + + // set the DV write out modes for f5 files [0 - regular DVs, 1 - ND, 2 - fail + // indexes] + void setWriteDVMode(int newMode) { writeDVmode = newMode; } + + // choose whether to use CPT (classical plate theory) analytical solution + // (True) or DOD experimental buckling solution (False) + void setCPTstiffenerCrippling(bool _CPTstiffenerCrippling) { + CPTstiffenerCrippling = _CPTstiffenerCrippling; + } + + // ============================================================================== + // Verification purpose public routines + // ============================================================================== + + /** + * @brief Compute the non-dimensional critical axial load for the global + * buckling of the stiffened panel (for output purposes only) + * @param rho_0 affine aspect ratio + * @param xi generalized rigidity + * @param gamma stiffener-to-panel 11-bending stiffness ratio + * @param zeta the transverse shear stiffness parameter + * + * @return TacsScalar The nondimensional critical axial load for the global + * buckling mode + */ + TacsScalar nondimCriticalGlobalAxialLoad(const TacsScalar rho_0, + const TacsScalar xi, + const TacsScalar gamma, + const TacsScalar zeta); + + /** + * @brief Compute the non-dimensional critical axial load for the local + * buckling of the stiffened panel (for output purposes only) + * @param rho_0 affine aspect ratio + * @param xi generalized rigidity + * @param zeta the transverse shear stiffness parameter + * + * @return TacsScalar The nondimensional critical axial load for the local + * buckling mode + */ + TacsScalar nondimCriticalLocalAxialLoad(const TacsScalar rho_0, + const TacsScalar xi, + const TacsScalar zeta); + + /** + * @brief Compute the non-dimensional critical shear load for the global + * buckling of the stiffened panel (for output purposes only) + * @param rho_0 affine aspect ratio + * @param xi generalized rigidity + * @param gamma stiffener-to-panel 11-bending stiffness ratio + * @param zeta the transverse shear stiffness parameter + * + * @return TacsScalar The nondimensional critical shear load for the global + * buckling mode + */ + TacsScalar nondimCriticalGlobalShearLoad(const TacsScalar rho_0, + const TacsScalar xi, + const TacsScalar gamma, + const TacsScalar zeta); + + /** + * @brief Compute the non-dimensional critical shear load for the local + * buckling of the stiffened panel (for output purposes only) + * @param rho_0 affine aspect ratio + * @param xi generalized rigidity + * @param zeta the transverse shear stiffness parameter + * + * @return TacsScalar The nondimensional critical shear load for the local + * buckling mode + */ + TacsScalar nondimCriticalLocalShearLoad(const TacsScalar rho_0, + const TacsScalar xi, + const TacsScalar zeta); + + /** + * @brief Compute the non-dimensional critical shear load for the local + * buckling of the stiffened panel (for output purposes only) + * @param rho_0 affine aspect ratio + * @param xi generalized rigidity + * @param genPoiss generalized poisson's ratio + * @param zeta the transverse shear stiffness parameter + * + * @return TacsScalar The nondimensional critical shear load for the local + * buckling mode + */ + TacsScalar nondimStiffenerCripplingLoad(const TacsScalar rho_0, + const TacsScalar xi, + const TacsScalar genPoiss, + const TacsScalar zeta); + + protected: + // ============================================================================== + // Override buckling load methods from super class + // ============================================================================== + + /** + * @brief Compute the strength ratio for the local buckling of the panel skin + * between stiffeners + * + * @param e Shell strains + * @return TacsScalar Strength ratio + */ + TacsScalar evalLocalPanelBuckling(const TacsScalar e[]) override; + + /** + * @brief Compute the strength ratio for the global buckling of the panel + * + * @param e Shell strains + * @return TacsScalar Strength ratio + */ + TacsScalar evalGlobalPanelBuckling(const TacsScalar e[]) override; + + /** + * @brief Compute the strength ratio with respect to stiffener crippling + * + * Uses methods described in section 8.5 of "Design and Analysis of Composite + * Structures with Application to Aerospace Structures, 2nd Edition" by + * Christos Kassapoglou for Ali's solution. + * + * Optional ML model included from Sean's work as well. + * + * @param stiffenerStrain Stiffener centroid beam strains + * @return TacsScalar Strength ratio + */ + TacsScalar evalStiffenerCrippling( + const TacsScalar stiffenerStrain[]) override; + + /** + * @brief Compute the sensitivity of the local panel buckling strength ratio + * + * @param e Shell strains + * @param sens Sensitivity of the output w.r.t the shell strains + * @return TacsScalar Strength Ratio + */ + TacsScalar evalLocalPanelBucklingStrainSens(const TacsScalar e[], + TacsScalar sens[]) override; + + /** + * @brief Compute the sensitivity of the global buckling strength ratio w.r.t + * the shell strains + * + * @param e Shell strains + * @param sens Sensitivity of the output w.r.t the shell strains + * @return TacsScalar Strength Ratio + */ + TacsScalar evalGlobalPanelBucklingStrainSens(const TacsScalar e[], + TacsScalar sens[]) override; + + /** + * @brief Compute the sensitivity of the stiffener crippling strength ratio + * w.r.t the stiffener strains + * + * @param stiffenerStrain stiffener shell strains strains + * @param sens Sensitivity of the output w.r.t the shell strains + * @return TacsScalar Strength Ratio + */ + TacsScalar evalStiffenerCripplingStrainSens( + const TacsScalar stiffenerStrain[], TacsScalar sens[]) override; + + /** + * @brief Add the derivative of the local panel buckling strength ratio w.r.t + * the design variables + + @param elemIndex The local element index (not used) + @param scale Value by which to scale the derivatives + @param pt The parametric point (not used) + @param X The physical node location (not used) + @param strain The shell strains + @param dvLen The length of the design vector (not used) + @param dfdx The DV sensitivity array to add to + */ + void addLocalPanelBucklingDVSens(int elemIndex, TacsScalar scale, + const double pt[], const TacsScalar X[], + const TacsScalar strain[], int dvLen, + TacsScalar dfdx[]) override; + + /** + * @brief Add the derivative of the global panel buckling strength ratio w.r.t + * the design variables + + @param elemIndex The local element index (not used) + @param scale Value by which to scale the derivatives + @param pt The parametric point (not used) + @param X The physical node location (not used) + @param strain The shell strains + @param dvLen The length of the design vector (not used) + @param dfdx The DV sensitivity array to add to + */ + void addGlobalPanelBucklingDVSens(int elemIndex, TacsScalar scale, + const double pt[], const TacsScalar X[], + const TacsScalar strain[], int dvLen, + TacsScalar dfdx[]) override; + + /** + * @brief Compute the sensitivity of the stiffener crippling strength ratio + * w.r.t the design variables + * + * @param scale the derivative scalar for the stiffener crippling failure + * index coming in + * @param stiffenerStrain the shell strains in the stiffener + * @param dfdx the output DV derivatives sensitivity + */ + void addStiffenerCripplingDVSens(const TacsScalar scale, + const TacsScalar stiffenerStrain[], + TacsScalar dfdx[]) override; + + // ============================================================================== + // Stiffener crippling helper functions + // ============================================================================== + + /** + * @brief compute the in plane load in the stiffener N11,stiff + * + * @param stiffenerStrain the midplane strains in the stiffener + * @param A11s the A11 material constant for the stiffener + * @return the stiffener in plane load N11,stiff + */ + TacsScalar computeStiffenerInPlaneLoad(const TacsScalar stiffenerStrain[], + TacsScalar* A11s); + + /** + * @brief compute the DV sensitivities of the stiffener in plane load + * N11,stiff + * + * @param scale the derivative df/dN11,stiff of the output in plane load + * @param stiffenerStrain the midplane strains eps11 in the stiffener shell + * @param dN11_stiff the jacobian dstiff_fail_index / dN11,stiff + * @param dfdx the final updated DV derivatives df/dx + */ + TacsScalar computeStiffenerInPlaneLoadSens(const TacsScalar scale, + const TacsScalar stiffenerStrain[], + const TacsScalar dN11_stiff, + TacsScalar dfdx[]); + + void computeStiffenerCripplingStiffness(TacsScalar C[]); + + // ============================================================================== + // Buckling functions + // ============================================================================== + + /** + * @brief Compute the non-dimensional affine aspect ratio rho_0 = a/b * + * (D22/D11)**0.25 + * + * @param D11 D11 stiffness + * @param D22 D22 stiffness + * @param a panel length + * @param b panel width + * + */ + static inline TacsScalar computeAffineAspectRatio(const TacsScalar D11, + const TacsScalar D22, + const TacsScalar a, + const TacsScalar b) { + return a / b * pow(D22 / D11, 0.25); + } + + /** + * @brief Compute the derivatives non-dimensional affine aspect ratio rho_0 = + * a/b * (D22/D11)**0.25 + * + * @param rho0sens backpropagated sensitivity for rho0 non-dimensional + * parameter + * @param D11 D11 stiffness + * @param D22 D22 stiffness + * @param a panel length + * @param b panel width + * @param D11sens Sensitivity w.r.t. the D11 stiffness + * @param D22sens Sensitivity w.r.t. the D22 stiffness + * @param asens Sensitivity w.r.t. the panel length a + * @param bsens Sensitivity w.r.t. the panel width b + * + */ + TacsScalar computeAffineAspectRatioSens( + const TacsScalar rho0sens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar a, const TacsScalar b, TacsScalar* D11sens, + TacsScalar* D22sens, TacsScalar* asens, TacsScalar* bsens); + + /** + * + * @brief Test the affine aspect ratio sensitivity. + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testAffineAspectRatio(const TacsScalar epsilon, int printLevel); + + /** + * @brief Compute the non-dimensional laminate isotropy xi = (D12 + 2 D66) + * / sqrt(D11 * D22) + * + * @param D11 D11 stiffness + * @param D22 D22 stiffness + * @param D12 D12 stiffness + * @param D66 D66 stiffness + * + */ + static inline TacsScalar computeLaminateIsotropy(const TacsScalar D11, + const TacsScalar D22, + const TacsScalar D12, + const TacsScalar D66) { + return (D12 + 2.0 * D66) / sqrt(D11 * D22); + } + + /** + * @brief Compute the derivatives of the non-dimensional laminate isotropy + * xi = (D12 + 2 D66) / sqrt(D11 * D22) + * + * @param xisens backpropagated sensitivity w.r.t. xi + * @param D11 D11 stiffness + * @param D22 D22 stiffness + * @param D12 D12 stiffness + * @param D66 D66 stiffness + * @param D11sens Sensitivity w.r.t. the D11 stiffness + * @param D22sens Sensitivity w.r.t. the D22 stiffness + * @param D12sens Sensitivity w.r.t. the D12 stiffness + * @param D66sens Sensitivity w.r.t. the D66 stiffness + * + */ + TacsScalar computeLaminateIsotropySens( + const TacsScalar xisens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar D12, const TacsScalar D66, TacsScalar* D11sens, + TacsScalar* D22sens, TacsScalar* D12sens, TacsScalar* D66sens); + + /** + * + * @brief Test the generalized rigidity sensitivity. + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testLaminateIsotropy(const TacsScalar epsilon, int printLevel); + + /** + * @brief Compute the non-dimensional generalized Poisson's ratio eps = 1/xi * + * D12 / sqrt(D11*D22) = (D12 + 2D66) / D12 + * + * @param D12 D12 stiffness + * @param D66 D66 stiffness + * + */ + static inline TacsScalar computeGeneralizedPoissonsRatio( + const TacsScalar D12, const TacsScalar D66) { + return D12 / (D12 + 2.0 * D66); + } + + /** + * @brief Compute the derivatives of the non-dimensional generalized Poisson's + * ratio eps = 1/xi * D12 / sqrt(D11*D22) = (D12 + 2D66) / D12 + * + * @param epssens backpropagated sensitivity for the output gen eps + * @param D12 D12 stiffness + * @param D66 D66 stiffness + * @param D12sens Sensitivity w.r.t. the D12 stiffness + * @param D66sens Sensitivity w.r.t. the D66 stiffness + * + */ + TacsScalar computeGeneralizedPoissonsRatioSens(const TacsScalar epssens, + const TacsScalar D12, + const TacsScalar D66, + TacsScalar* D12sens, + TacsScalar* D66sens); + + /** + * + * @brief Test the generalized poisson's ratio sensitivity. + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testGeneralizedPoissonsRatio(const TacsScalar epsilon, + int printLevel); + + /** + * @brief Compute the non-dimensional stiffener area ratio delta = E1s * As / + * (E1p * s_p * h) based on the DVs => stiffenerPitch, panelThick, + * stiffenerHeight, stiffenerThick + * + * @param E1p effective E11p modulus + * @param E1s effective E11s modulus + */ + TacsScalar computeStiffenerAreaRatio(const TacsScalar E1p, + const TacsScalar E1s); + + /** + * @brief Compute the sensitivities of the non-dimensional stiffener area + * ratio delta = E1s * As / (E1p * s_p * h) + * + * @param deltasens backpropagated sensitivity w.r.t the stiffener area ratio + * delta + * @param E1p effective E11p modulus + * @param E1s effective E11s modulus + * @param sthickSens stiffener thickness sens + * @param sheightSens stiffener height sens + * @param spitchSens stiffener pitch sens + * @param pthickSens panel thickness sens + * @param E1pSens effective E11p modulus sens + * @param E1sSens effective E11s modulus sens + * + */ + TacsScalar computeStiffenerAreaRatioSens( + const TacsScalar deltasens, const TacsScalar E1p, const TacsScalar E1s, + TacsScalar* sthickSens, TacsScalar* sheightSens, TacsScalar* spitchSens, + TacsScalar* pthickSens, TacsScalar* E1psens, TacsScalar* E1ssens); + + /** + * + * @brief Test the stiffener area ratio sensitivity aka delta parameter + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testStiffenerAreaRatio(const TacsScalar epsilon, int printLevel); + + /** + * @brief Compute the non-dimensional stiffener-to-panel stiffness ratio gamma + * = E1s * Is / (sp * D11) based on the DVs => stiffenerPitch, panelThick, + * stiffenerHeight, stiffenerThick + * + * @param D11 the D11 stiffness of the plate + * @param E1s the E1s effective stiffener modulus + * @param zn the overall centroid + */ + TacsScalar computeStiffenerStiffnessRatio(const TacsScalar D11, + const TacsScalar E1s, + const TacsScalar zn); + + /** + * @brief Compute the sensitivities of the non-dimensional stiffener-to-panel + * stiffness ratio gamma = E1s * Is / (sp * D11) + * + * @param gammaSens backpropagated derivative through gamma output + * @param D11 the D11 stiffness of the plate + * @param E1s the E1s effective stiffener modulus + * @param zn the overall centroid + * @param D11Sens sensitivity w.r.t. the D11 stiffness + * @param sthickSens stiffener thickness sens + * @param sheightSens stiffener height sens + * @param spitchSens stiffener pitch sens + * @param E1sSens E1s effective stiffener modulus sens + * @param znSens overall centroid sens + * + */ + TacsScalar computeStiffenerStiffnessRatioSens( + const TacsScalar gammaSens, const TacsScalar D11, const TacsScalar E1s, + const TacsScalar zn, TacsScalar* D11Sens, TacsScalar* sthickSens, + TacsScalar* sheightSens, TacsScalar* spitchSens, TacsScalar* E1sSens, + TacsScalar* znSens); + + /** + * + * @brief Test the stiffener stiffness ratio sensitivity aka gamma parameter + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testStiffenerStiffnessRatio(const TacsScalar epsilon, + int printLevel); + + /** + * @brief Compute the non-dimensional transverse shear parameter + * zeta = A11 / A66 * (h/b)**2 + * + * @param A66 the A66 stiffness of the plate + * @param A11 the A11 stiffness of the plate + * @param b the panel widtdh + * @param h the panel height + * + */ + TacsScalar computeTransverseShearParameter(TacsScalar A66, TacsScalar A11, + TacsScalar b, TacsScalar h); + + /** + * @brief Compute the sensitivities of the non-dimensional transverse shear + * parameter zeta = A11 / A66 * (h/b)**2 + * + * @param zetasens backpropagated sensitivity for output zeta + * @param A66 the A66 stiffness of the plate + * @param A11 the A11 stiffness of the plate + * @param b the panel widtdh + * @param h the panel height + * @param A66sens sensitivity w.r.t the A66 stiffness of the plate + * @param A11sens sensitivity w.r.t the A11 stiffness of the plate + * @param bsens sensitivity w.r.t the panel widtdh + * @param hsens sensitivity w.r.t the panel height + * + */ + TacsScalar computeTransverseShearParameterSens( + const TacsScalar zetasens, const TacsScalar A66, const TacsScalar A11, + const TacsScalar b, const TacsScalar h, TacsScalar* A66sens, + TacsScalar* A11sens, TacsScalar* bsens, TacsScalar* hsens); + + /** + * + * @brief Test the transverse shear parameter sensitivity aka zeta parameter + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testTransverseShearParameter(const TacsScalar epsilon, + int printLevel); + + /** + * @brief Compute the critical axial load for the global buckling of the + * stiffened panel + * @param D11 D11 stiffness of the plate + * @param D22 D22 stiffness of the plate + * @param b panel width + * @param delta stiffener-to-(local panel) area ratio + * @param rho_0 affine aspect ratio + * @param xi generalized rigidity + * @param gamma stiffener-to-panel 11-bending stiffness ratio + * @param zeta the transverse shear stiffness parameter + * + * @return TacsScalar The critical axial load for the global buckling mode + */ + TacsScalar computeCriticalGlobalAxialLoad( + const TacsScalar D11, const TacsScalar D22, const TacsScalar b, + const TacsScalar delta, const TacsScalar rho_0, const TacsScalar xi, + const TacsScalar gamma, const TacsScalar zeta); + + /* + * computes the overall centroid zc of the panel and stiffener cross-section + * in the 23-plane + * + * @param + */ + TacsScalar computeOverallCentroid(const TacsScalar E1p, const TacsScalar E1s); + + void computeOverallCentroidSens(const TacsScalar znSens, const TacsScalar E1p, + const TacsScalar E1s, TacsScalar* sthickSens, + TacsScalar* sheightSens, + TacsScalar* pthickSens, + TacsScalar* spitchSens, TacsScalar* E1pSens, + TacsScalar* E1sSens); + + /** + * @brief Compute the sensitivities w.r.t. the critical axial load + * for the global buckling of the stiffened panel + * @param N1sens backpropagated derivative through N1crit computation of + * output + * @param D11 D11 stiffness of the plate + * @param D22 D22 stiffness of the plate + * @param b panel width + * @param delta stiffener-to-(local panel) area ratio + * @param rho_0 affine aspect ratio + * @param xi generalized rigidity + * @param gamma stiffener-to-panel 11-bending stiffness ratio + * @param zeta the transverse shear stiffness parameter + * @param D11sens Sensitivity w.r.t. the D11 stiffness + * @param D22sens Sensitivity w.r.t. D22 stiffness + * @param bsens Sensitivity w.r.t. panel width + * @param deltasens Sensitivity w.r.t. stiffener-to-(local panel) area ratio + * @param rho_0sens Sensitivity w.r.t. affine aspect ratio + * @param xisens Sensitivity w.r.t. generalized rigidity + * @param gammasens Sensitivity w.r.t. stiffener-to-panel 11-bending stiffness + * @param zetasens Sensitivity w.r.t.the transverse shear stiffness parameter + * ratio + * + * @return TacsScalar The critical axial load for the global buckling mode + */ + TacsScalar computeCriticalGlobalAxialLoadSens( + const TacsScalar N1sens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar b, const TacsScalar delta, const TacsScalar rho_0, + const TacsScalar xi, const TacsScalar gamma, const TacsScalar zeta, + TacsScalar* D11sens, TacsScalar* D22sens, TacsScalar* bsens, + TacsScalar* deltasens, TacsScalar* rho_0sens, TacsScalar* xisens, + TacsScalar* gammasens, TacsScalar* zetasens); + + /** + * + * @brief Test the critical global axial load function + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testCriticalGlobalAxialLoad(const TacsScalar epsilon, + int printLevel); + + /** + * + * @brief Test the overall centroid method + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testOverallCentroid(const TacsScalar epsilon, int printLevel); + + /** + * + * @brief Test the computation of D11p with overall centroid + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testPanelGlobalBucklingStiffness(const TacsScalar epsilon, + int printLevel); + + /** + * + * @brief Test the other util tests + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testOtherTests(const TacsScalar epsilon, int printLevel); + + /** + * @brief Compute the critical axial load for the local buckling mode of the + * stiffened panel + * @param D11 D11 stiffness of the plate + * @param D22 D22 stiffness of the plate + * @param rho_0 affine aspect ratio + * @param xi generalized rigidity + * @param zeta Transverse shear stiffness parameter + * + * @return TacsScalar The critical axial load for the local buckling mode + */ + TacsScalar computeCriticalLocalAxialLoad(const TacsScalar D11, + const TacsScalar D22, + const TacsScalar rho_0, + const TacsScalar xi, + const TacsScalar zeta); + + /** + * @brief Compute the sensitivities w.r.t. the critical axial load + * for the global buckling of the stiffened panel + * @param N1sens backpropagated derivative through N1crit computation of local + * mode + * @param D11 D11 stiffness of the plate + * @param D22 D22 stiffness of the plate + * @param rho_0 affine aspect ratio + * @param xi generalized rigidity + * @param zeta Transverse shear stiffness parameter + * @param D11sens Sensitivity w.r.t. the D11 stiffness + * @param D22sens Sensitivity w.r.t. D22 stiffness + * @param spitchsens Sensitivity w.r.t. the stiffener pitch + * @param rho_0sens Sensitivity w.r.t. affine aspect ratio + * @param xisens Sensitivity w.r.t. generalized rigidity + * @param zetasens Sensitivity w.r.t. the transverse shear stiffness parameter + * + * @return TacsScalar The critical axial load for the global buckling mode + */ + TacsScalar computeCriticalLocalAxialLoadSens( + const TacsScalar N1sens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar rho_0, const TacsScalar xi, const TacsScalar zeta, + TacsScalar* D11sens, TacsScalar* D22sens, TacsScalar* spitchsens, + TacsScalar* rho_0sens, TacsScalar* xisens, TacsScalar* zetasens); + + /** + * + * @brief Test the critical local axial load function + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testCriticalLocalAxialLoad(const TacsScalar epsilon, + int printLevel); + + /** + * @brief Compute the critical shear load for global buckling + * + * + * input: + * @param D11 the D11 stiffness of the plate + * @param D22 the D22 stiffness of the plate + * @param b the panel width + * @param rho_0 the affine aspect ratio + * @param xi the generalized rigidity + * @param gamma the stiffener-to-(local panel) bending stiffness ratio + * @param zeta the transverse shear stiffness parameter + * + * returns: + * @return N12Crit the approximate critical buckling load + */ + TacsScalar computeCriticalGlobalShearLoad( + const TacsScalar D11, const TacsScalar D22, const TacsScalar b, + const TacsScalar rho_0, const TacsScalar xi, const TacsScalar gamma, + const TacsScalar zeta); + + /** + * @brief Compute the sensitivity of the critical shear load for global + * buckling + * + * @param N12sens the backpropagated sensitivity of the output N12crit + * @param D11 the D11 stiffness of the plate + * @param D22 the D22 stiffness of the plate + * @param b the panel width + * @param rho_0 the generalized affine aspect ratio + * @param xi the generalized rigidity + * @param gamma the stiffener-to-(local panel) bending stiffness ratio + * @param zeta the transverse shear stiffness parameter + * @param D11sens sensitivity w.r.t. the D11 stiffness of the plate + * @param D22sens sensitivity w.r.t. the D22 stiffness of the plate + * @param bsens sensitivity w.r.t. the panel width + * @param rho_0sens sensitivity w.r.t. the affine aspect ratio + * @param xisens sensitivity w.r.t. the generalized rigidity + * @param gammasens sensitivity w.r.t. the stiffener-to-(local panel) bending + * @param zetasens sensitivity w.r.t. the transverse shear stiffness parameter + * stiffness ratio + * @return TacsScalar The critical shear buckling load + */ + TacsScalar computeCriticalGlobalShearLoadSens( + const TacsScalar N12sens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar b, const TacsScalar rho_0, const TacsScalar xi, + const TacsScalar gamma, const TacsScalar zeta, TacsScalar* D11sens, + TacsScalar* D22sens, TacsScalar* bsens, TacsScalar* rho_0sens, + TacsScalar* xisens, TacsScalar* gammasens, TacsScalar* zetasens); + + /** + * + * @brief Test the critical global shear load function + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testCriticalGlobalShearLoad(const TacsScalar epsilon, + int printLevel); + + /** + * + * @brief Test the critical global shear load function at low rho0 + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testCriticalGlobalShearLoad_LowAR(const TacsScalar epsilon, + int printLevel); + + /** + * @brief Compute the critical shear load for local buckling + * + * + * input: + * @param D11 the D11 stiffness of the plate + * @param D22 the D22 stiffness of the plate + * @param rho_0 the affine aspect ratio + * @param xi the generalized rigidity + * @param zeta the transverse shear stiffness parameter + * + * returns: + * @return N12Crit the approximate critical buckling load + */ + TacsScalar computeCriticalLocalShearLoad(const TacsScalar D11, + const TacsScalar D22, + const TacsScalar rho_0, + const TacsScalar xi, + const TacsScalar zeta); + + /** + * @brief Compute the sensitivity of the critical shear buckling load (local + * mode) + * + * @param N12sens backpropagated sensitivity w.r.t the output N12crit + * computation + * @param D11 the D11 stiffness of the plate + * @param D22 the D22 stiffness of the plate + * @param b the panel width + * @param rho0 the affine aspect ratio + * @param xi the generalized rigidity + * @param gamma the stiffener-to-(local panel) bending stiffness ratio + * @param D11sens sensitivity w.r.t. the D11 stiffness of the plate + * @param D22sens sensitivity w.r.t. the D22 stiffness of the plate + * @param spitchsens sensitivity w.r.t. the stiffener pitch + * @param rho_0sens sensitivity w.r.t. the affine aspect ratio + * @param xisens sensitivity w.r.t. the generalized rigidity + * @param zeta sensitivity w.r.t. the transverse shear stiffness parameter + * @return TacsScalar The critical shear buckling load + */ + TacsScalar computeCriticalLocalShearLoadSens( + const TacsScalar N12sens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar rho_0, const TacsScalar xi, const TacsScalar zeta, + TacsScalar* D11sens, TacsScalar* D22sens, TacsScalar* spitchsens, + TacsScalar* rho_0sens, TacsScalar* xisens, TacsScalar* zetasens); + + /** + * + * @brief Test the critical local shear load function at high AR rho0 + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testCriticalLocalShearLoad(const TacsScalar epsilon, + int printLevel); + + /** + * + * @brief Test the critical local shear load function at low AR rho0 + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testCriticalLocalShearLoad_LowAR(const TacsScalar epsilon, + int printLevel); + + /** + * @brief Compute the non-dimensional parameters lam1bar, lam2bar using Newton + * iteration for the critical shear load closed-form solution. + * + * @param xi the non-dimensional generalized rigidity of the plate + * @param gamma the stiffener-to-plate stiffness ratio + * @param lam1bar return value of lam1bar + * @param lam2bar return value of lam2bar which is always positive since it + * shows up as squared + */ + void nondimShearParams(const TacsScalar xi, const TacsScalar gamma, + TacsScalar* lam1bar, TacsScalar* lam2bar); + + /** + * @brief Compute the panel global buckling constant D11 adjusted for the + * overall centroid of the panel & stiffener + * + * @param E1p the effective modulus of the panel Q11 - Q12^2 / Q22 + * @param zn the overall centroid in the 23-plane or 1-plane of the panel & + * stiffeners + * @return the overall centroid-adjusted D11 constant + */ + void computePanelGlobalBucklingStiffness(const TacsScalar E1p, + const TacsScalar zn, TacsScalar* D1); + + /** + * @brief compute sensitivities of the overall centroid-adjusted D11 material + * constant + * + * @param D1Sens the derivative at the D11 level df/dD11, input for + * backpropagation + * @param E1p the effective modulus of the panel Q11 - Q12^2 / Q22 + * @param zn the overall centroid in the 23-plane or 1-plane of the panel & + * stiffeners + * @param spitchsens the output sensitivity of stiffener pitch df/dspitch + * @param pthicksens the output sensitivity of panel thickness df/dpthick + * @param E1pSens the output sensitivity of effective modulus of the panel + * df/dE1p + * @param znSens the output sensitivity of the overall centroid, df/dzn + */ + void computePanelGlobalBucklingStiffnessSens( + const TacsScalar D1Sens, const TacsScalar E1p, const TacsScalar zn, + TacsScalar* spitchSens, TacsScalar* pthickSens, TacsScalar* E1pSens, + TacsScalar* znSens); + + /** + * @brief Compute the derivatives of lam2sq for the critical shear load + * closed-form solution. + * + * @param xi the non-dimensional generalized rigidity of the plate + * @param gamma the stiffener-to-plate stiffness ratio + * @param lam1bar return value of lam1bar + * @param lam2bar return value of lam2bar which is always positive since it + * shows up as squared + * @param dl1xi dlam1_bar/dxi sens + * @param dl1gamma dlam1_bar/dgamma sens + * @param dl2xi dlam2_bar/dxi sens + * @param dl2gamma dlam2_bar/dgamma sens + */ + void nondimShearParamsSens(const TacsScalar xi, const TacsScalar gamma, + TacsScalar* lam1bar, TacsScalar* lam2bar, + TacsScalar* dl1xi, TacsScalar* dl1gamma, + TacsScalar* dl2xi, TacsScalar* dl2gamma); + + /** + * + * @brief Test the nondimShearParams function used in the shear buckling loads + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testNondimShearParams(const TacsScalar epsilon, int printLevel); + + /** + * @brief the constraint for shear buckling closed-form solution R(lam1, lam2) + * which is reformulated as constraint on just lam2_bar R(lam2_bar) + * + * @param lam2sq input lam2_bar^2 non-dimensional mode shape parameter for the + * shear mode + * @param xi the non-dimensional laminate isotropy + * @param gamma the non-dimensional stiffener stiffness ratio + * @return the residual R(lam2_bar^2) + */ + TacsScalar lam2Constraint(const TacsScalar lam2sq, const TacsScalar xi, + const TacsScalar gamma); + + /** + * @brief the derivative of the shear buckling closed-form solution constraint + * reformulated as R(lam2_bar) = 0 + * + * @param lam2sq input lam2_bar^2 non-dimensional mode shape parameter for the + * shear mode + * @param xi the non-dimensional laminate isotropy + * @param gamma the non-dimensional stiffener stiffness ratio + * @return returns the Jacobian of the constraint, dR/dlam2_bar + */ + TacsScalar lam2ConstraintDeriv(const TacsScalar lam2sq, const TacsScalar xi, + const TacsScalar gamma); + + /** + * + * @brief Test the lam2Constraint function used in the shear buckling + * closed-form solution + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error of the derivative test + */ + TacsScalar testLam2Constraint(const TacsScalar epsilon, int printLevel); + + /** + * @brief Compute the critical stiffener crippling load + * + * + * input: + * @param D11 the D11 stiffness of the stiffener + * @param D22 the D22 stiffness of the stiffener + * @param xi the generalized rigidity + * @param rho_0 the affine aspect ratio + * @param genPoisss the generalized Poisson's ratio of the stiffener + * @param zeta the transverse shear stiffness parameter + * + * returns: + * @return N11crit the approximate critical crippling in-plane load of the + * stiffener + */ + TacsScalar computeStiffenerCripplingLoad( + const TacsScalar D11, const TacsScalar D22, const TacsScalar xi, + const TacsScalar rho_0, const TacsScalar genPoiss, const TacsScalar zeta); + + /** + * @brief Compute the sensitivity of the critical stiffener crippling load + * + * @param N1sens backpropagated sensitivity w.r.t. the stiffener crit load + * N1crit + * @param D11 the D11 stiffness of the stiffener + * @param D22 the D22 stiffness of the stiffener + * @param xi the generalized rigidity + * @param rho_0 the affine aspect ratio + * @param genPoisss the generalized Poisson's ratio of the stiffener + * @param zeta the transverse shear stiffness parameter + * @param D11sens Sensitivity of the the D11 stiffness of the stiffener + * @param D22sens Sensitivity of the the D22 stiffness of the stiffener + * @param sheightsens Sensitivity w.r.t. the stiffener height DV + * @param xisens Sensitivity of the the generalized rigidity + * @param rho_0sens Sensitivity w.r.t the affine aspect ratio + * @param genPoiss_sens Sensitivity of the the generalized Poisson's ratio of + * the stiffener + * @param zetasens Sensitivity w.r.t the transverse shear stiffness parameter + * @return N11crit the approximate critical crippling in-plane load of the + * stiffener + */ + TacsScalar computeStiffenerCripplingLoadSens( + const TacsScalar N1sens, const TacsScalar D11, const TacsScalar D22, + const TacsScalar xi, const TacsScalar rho_0, const TacsScalar genPoiss, + const TacsScalar zeta, TacsScalar* D11sens, TacsScalar* D22sens, + TacsScalar* sheightsens, TacsScalar* xisens, TacsScalar* rho_0sens, + TacsScalar* genPoiss_sens, TacsScalar* zetasens); + + // ============================================================================== + // Attributes + // ============================================================================== + + // Machine learning Gaussian Process models stored in panel GP class + TACSPanelGPs* panelGPs; + + // --- Design variable values --- + TacsScalar panelWidth; ///< Panel width + static const int NUM_CF_MODES = + 50; // number of modes used in closed-form method + + TacsScalar panelWidthLowerBound = 0.0; + TacsScalar panelWidthUpperBound = 1e20; + + // --- Design variable numbers --- + int panelWidthNum; ///< Panel width DV number + int panelWidthLocalNum; ///< Panel width local DV number + + // stiffener crippling prediction + bool CPTstiffenerCrippling = false; + + // debugging modes + int writeDVmode = 0; // 0 - normal DVs, 1 - NDparams + + // pointers for Xtest vectors used in GP computation + TacsScalar *XtestAxial, *XtestShear, *XtestCrippling; + TacsScalar *XtestAxialSens, *XtestShearSens, *XtestCripplingSens; + + private: + // private so that subclass constName for GP buckling constraints doesn't + // conflict with superclass + static const char* constName; ///< Constitutive model name +}; diff --git a/src/constitutive/TACSGaussianProcessModel.cpp b/src/constitutive/TACSGaussianProcessModel.cpp new file mode 100644 index 000000000..ae49eb229 --- /dev/null +++ b/src/constitutive/TACSGaussianProcessModel.cpp @@ -0,0 +1,353 @@ +#include "TACSGaussianProcessModel.h" + +TACSGaussianProcessModel::TACSGaussianProcessModel(int n_train, int n_param, + const TacsScalar Xtrain[], + const TacsScalar alpha[], + const TacsScalar theta[]) { + // constructor for the base TACSGaussianProcessModel class + this->n_train = n_train; + this->n_param = n_param; + + // create a new copy of the Xtrain, alpha arrays on this process + this->Xtrain = new TacsScalar[n_train * n_param]; + for (int ii = 0; ii < n_train * n_param; ii++) { + this->Xtrain[ii] = Xtrain[ii]; + } + + this->alpha = new TacsScalar[n_train]; + for (int jj = 0; jj < n_train; jj++) { + this->alpha[jj] = alpha[jj]; + } + + this->theta = new TacsScalar[n_theta]; + for (int kk = 0; kk < n_theta; kk++) { + this->theta[kk] = theta[kk]; + } + + this->ks = 10.0; +} + +TACSGaussianProcessModel::~TACSGaussianProcessModel() { + // destructor for the base TACSGaussianProcessModel class + delete[] this->Xtrain; + this->Xtrain = nullptr; + + delete[] this->alpha; + this->alpha = nullptr; + + delete[] this->theta; + this->theta = nullptr; +} + +TacsScalar TACSGaussianProcessModel::predictMeanTestData( + const TacsScalar* Xtest) { + // Xtest is an array of size n_param (for one test data point) + // use the equation mean(Ytest) = cov(Xtest,X_train) @ alpha [this is a dot + // product] where Ytest is a scalar + TacsScalar Ytest = 0.0; + + // iterate over each training data point, get the cross-term covariance and + // add the coefficient alpha for it + for (int itrain = 0; itrain < n_train; itrain++) { + TacsScalar* loc_Xtrain = &Xtrain[n_param * itrain]; + // TacsScalar mkernel = kernel(Xtest, loc_Xtrain); + Ytest += kernel(Xtest, loc_Xtrain) * alpha[itrain]; + } + return Ytest; +} + +TacsScalar TACSGaussianProcessModel::predictMeanTestDataSens( + const TacsScalar Ysens, const TacsScalar* Xtest, TacsScalar* Xtestsens) { + // Xtest is an array of size n_param (for one test data point) + // the sensitivity here is on log[nondim-params] + // use the equation mean(Ytest) = cov(Xtest,X_train) @ alpha [this is a dot + // product] where Ytest is a scalar + TacsScalar Ytest = 0.0; + + // iterate over each training data point, get the cross-term covariance and + // add the coefficient alpha for it + memset(Xtestsens, 0, n_param * sizeof(TacsScalar)); + + for (int itrain = 0; itrain < n_train; itrain++) { + TacsScalar* loc_Xtrain = &Xtrain[n_param * itrain]; + Ytest += kernel(Xtest, loc_Xtrain) * alpha[itrain]; + + // backwards propagate to the Xtestsens through the kernel computation + kernelSens(Ysens * alpha[itrain], Xtest, loc_Xtrain, Xtestsens); + } + + return Ytest; +} + +TacsScalar TACSBucklingGaussianProcessModel::kernel(const TacsScalar* Xtest, + const TacsScalar* Xtrain) { + // define the kernel function k(*,*) on training and testing points for one + // training point the entries are [log(1+xi), log(rho_0), log(1+gamma), + // log(1+10^3*zeta)] + + TacsScalar d2 = Xtest[2] - Xtrain[2]; + TacsScalar d_gamma_rho_train = Xtrain[1] - theta[0] * Xtrain[2]; + TacsScalar d_gamma_rho_test = Xtest[1] - theta[0] * Xtest[2]; + + TacsScalar asymlinear_kernel = + theta[1] + theta[2] * soft_relu(-d_gamma_rho_train, ks) * + soft_relu(-d_gamma_rho_test, ks); + TacsScalar gamma_kernel = 1.0 + theta[3] * Xtrain[2] * Xtest[2]; + TacsScalar xi_kernel = + 1.0 + Xtrain[0] * Xtest[0] * (theta[4] + theta[5] * Xtrain[0] * Xtest[0]); + TacsScalar zeta_kernel = + Xtrain[3] * Xtest[3] * (theta[6] + theta[7] * Xtrain[3] * Xtest[3]); + + TacsScalar arg1 = (d_gamma_rho_test - d_gamma_rho_train) / theta[9]; + TacsScalar arg2 = d2 / theta[10]; + TacsScalar SE_kernel = theta[8] * exp(-0.5 * arg1 * arg1 - 0.5 * arg2 * arg2); + TacsScalar SE_window = + soft_relu(theta[11] - soft_abs(d_gamma_rho_train, ks), ks) * + soft_relu(theta[11] - soft_abs(d_gamma_rho_test, ks), ks); + + return asymlinear_kernel * gamma_kernel * xi_kernel + zeta_kernel + + SE_kernel * SE_window; +} + +void TACSBucklingGaussianProcessModel::kernelSens(const TacsScalar ksens, + const TacsScalar* Xtest, + const TacsScalar* Xtrain, + TacsScalar* Xtestsens) { + // add into the Xtestsens (don't reset to zero) for x_test = log[nondim + // params] vector + + // forward analysis section + // ----------------------------------- + // training point the entries are [log(1+xi), log(rho_0), log(1+gamma), + // log(1+10^3*zeta)] + + TacsScalar d2 = Xtest[2] - Xtrain[2]; + TacsScalar d_gamma_rho_train = Xtrain[1] - theta[0] * Xtrain[2]; + TacsScalar d_gamma_rho_test = Xtest[1] - theta[0] * Xtest[2]; + + TacsScalar asymlinear_kernel = + theta[1] + theta[2] * soft_relu(-d_gamma_rho_train, ks) * + soft_relu(-d_gamma_rho_test, ks); + TacsScalar gamma_kernel = 1.0 + theta[3] * Xtrain[2] * Xtest[2]; + TacsScalar xi_kernel = + 1.0 + Xtrain[0] * Xtest[0] * (theta[4] + theta[5] * Xtrain[0] * Xtest[0]); + TacsScalar zeta_kernel = + Xtrain[3] * Xtest[3] * (theta[6] + theta[7] * Xtrain[3] * Xtest[3]); + + TacsScalar SE_kernel = + theta[8] * + exp(-0.5 * pow((d_gamma_rho_test - d_gamma_rho_train) / theta[9], 2.0) - + 0.5 * pow(d2 / theta[10], 2.0)); + TacsScalar SE_window = + soft_relu(theta[11] - soft_abs(d_gamma_rho_train, ks), ks) * + soft_relu(theta[11] - soft_abs(d_gamma_rho_test, ks), ks); + + TacsScalar output = asymlinear_kernel * gamma_kernel * xi_kernel + + zeta_kernel + SE_kernel * SE_window; + + // sensitivity section + // --------------------------------------------------- + // hold derivatives w.r.t. Xtest[0], ..., Xtest[3] of the kernel + TacsScalar jacobian[4]; + memset(jacobian, 0.0, 4 * sizeof(TacsScalar)); + + // jacobian of x_xi = log(xi) direction 0 + TacsScalar xi_kernel_sens = + theta[4] * Xtrain[0] + 2.0 * theta[5] * Xtrain[0] * Xtrain[0] * Xtest[0]; + jacobian[0] = xi_kernel_sens * asymlinear_kernel * gamma_kernel; + + // log(rho_0) direction 1 + TacsScalar asymlinear_kernel_sens_rho0 = + theta[2] * soft_relu(-d_gamma_rho_train, ks) * + soft_relu_sens(-d_gamma_rho_test, ks) * -1.0; + TacsScalar SE_kernel_sens_rho0 = SE_kernel * -1.0 * + (d_gamma_rho_test - d_gamma_rho_train) / + theta[9] / theta[9]; + TacsScalar SE_window_sens_rho0 = + soft_relu(theta[11] - soft_abs(d_gamma_rho_train, ks), ks) * + soft_relu_sens(theta[11] - soft_abs(d_gamma_rho_test, ks), ks) * -1.0 * + soft_abs(d_gamma_rho_test, ks); + jacobian[1] = asymlinear_kernel_sens_rho0 * gamma_kernel * xi_kernel + + SE_kernel_sens_rho0 * SE_window + + SE_kernel * SE_window_sens_rho0; + + // log(1+gamma) direction 2 + TacsScalar asymlinear_kernel_sens_gam = + theta[2] * soft_relu(-d_gamma_rho_train, ks) * + soft_relu_sens(-d_gamma_rho_test, ks) * -1.0 * -theta[0]; + TacsScalar SE_kernel_sens_gam = SE_kernel * -1.0 * + (d_gamma_rho_test - d_gamma_rho_train) / + theta[9] / theta[9] * -theta[0]; + TacsScalar SE_window_sens_gam = + soft_relu(theta[11] - soft_abs(d_gamma_rho_train, ks), ks) * + soft_relu_sens(theta[11] - soft_abs(d_gamma_rho_test, ks), ks) * -1.0 * + soft_abs(d_gamma_rho_test, ks) * -theta[0]; + TacsScalar gamma_kernel_sens = theta[3] * Xtrain[2]; + jacobian[2] = asymlinear_kernel_sens_gam * gamma_kernel * xi_kernel + + asymlinear_kernel * gamma_kernel_sens * xi_kernel + + SE_kernel_sens_gam * SE_window + SE_kernel * SE_window_sens_gam; + + // log(zeta) direction 3 + TacsScalar zeta_kernel_sens = + theta[6] * Xtrain[3] + theta[7] * Xtest[3] * 2.0 * pow(Xtrain[3], 2.0); + jacobian[3] = zeta_kernel_sens; + + // scale up the Xtestsens by the backpropagated values + for (int ii = 0; ii < 4; ii++) { + Xtestsens[ii] += ksens * jacobian[ii]; + } +} + +TacsScalar TACSGaussianProcessModel::testAllGPTests(TacsScalar epsilon, + int printLevel) { + // run all GP tests + const int n_tests = 4; + TacsScalar relErrors[n_tests]; + + relErrors[0] = test_soft_relu(epsilon); + relErrors[1] = test_soft_abs(epsilon); + relErrors[2] = testPredictMeanTestData(epsilon, printLevel); + relErrors[3] = testKernelSens(epsilon, printLevel); + + // get max rel error among them + TacsScalar maxRelError = 0.0; + for (int i = 0; i < n_tests; i++) { + if (TacsRealPart(relErrors[i]) > TacsRealPart(maxRelError)) { + maxRelError = relErrors[i]; + } + } + + // get max rel error among them + if (printLevel != 0) { + printf("\ntestAllGPtests full results::\n"); + printf("\ttest_soft_relu = %.4e\n", TacsRealPart(relErrors[0])); + printf("\ttest_soft_abs = %.4e\n", TacsRealPart(relErrors[1])); + printf("\ttestPredictMeanTestData = %.4e\n", TacsRealPart(relErrors[2])); + printf("\ttestKernelSens = %.4e\n", TacsRealPart(relErrors[3])); + printf("\tOverall max rel error = %.4e\n\n", TacsRealPart(maxRelError)); + } + + return maxRelError; +} + +TacsScalar TACSGaussianProcessModel::testPredictMeanTestData(TacsScalar epsilon, + int printLevel) { + // test the sensitivities of the kernel computation + + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = this->n_param; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // compute initial values + for (int i0 = 0; i0 < n_input; i0++) { + x0[i0] = ((double)rand() / (RAND_MAX)); + } + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + f0 = predictMeanTestData(x); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + f2 = predictMeanTestData(x); + + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + for (int i = 0; i < n_input; i++) { + x[i] = x0[i]; + } + predictMeanTestDataSens(p_output, x, input_sens); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\t%s..testPredictMeanTestDataSens:\n", typeid(this).name()); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + + return relError; +} + +TacsScalar TACSBucklingGaussianProcessModel::testKernelSens(TacsScalar epsilon, + int printLevel) { + // test the sensitivities of the kernel computation + + // perform complex-step or finite difference check (depending on the value of + // _eps/epsilon) generate random input perturbation and output perturbation + // test vectors + const int n_input = 4; + TacsScalar p_input[n_input], x0[n_input], x[n_input], input_sens[n_input]; + for (int ii = 0; ii < n_input; ii++) { + p_input[ii] = ((double)rand() / (RAND_MAX)); + } + + TacsScalar p_output = ((double)rand() / (RAND_MAX)); + + // temp test only certain kernel derivs + // p_input[0] = p_input[1] = p_input[2] = 0.0; + + // compute initial values + x0[0] = 0.43243; // log(xi) + x0[1] = 0.9847; // log(rho0) + x0[2] = 0.12345; // log(1+gamma) + x0[3] = 0.53432; // log(zeta) + + // perform central difference over rho_0 function on [D11,D22,a,b] + TacsScalar f0, f1, f2; + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] - p_input[i] * epsilon; + } + TacsScalar* Xtrain = this->Xtrain; + f0 = kernel(x, Xtrain); + + for (int i = 0; i < n_input; i++) { + x[i] = x0[i] + p_input[i] * epsilon; + } + f2 = kernel(x, Xtrain); + + TacsScalar temp = p_output * (f2 - f0); + TacsScalar centralDiff = p_output * (f2 - f0) / 2.0 / epsilon; + + // now perform the adjoint sensitivity + memset(input_sens, 0, n_input * sizeof(TacsScalar)); + for (int i = 0; i < n_input; i++) { + x[i] = x0[i]; + } + kernelSens(p_output, x, Xtrain, input_sens); + TacsScalar adjTD = 0.0; + for (int j = 0; j < n_input; j++) { + adjTD += input_sens[j] * p_input[j]; + } + adjTD = TacsRealPart(adjTD); + + // compute relative error + TacsScalar relError = abs((adjTD - centralDiff) / centralDiff); + if (printLevel != 0) { + printf("\t%s..testKernelSens:\n", typeid(this).name()); + printf("\t\t adjDeriv = %.4e\n", TacsRealPart(adjTD)); + printf("\t\t centralDiff = %.4e\n", TacsRealPart(centralDiff)); + printf("\t\t rel error = %.4e\n", TacsRealPart(relError)); + } + return relError; +} diff --git a/src/constitutive/TACSGaussianProcessModel.h b/src/constitutive/TACSGaussianProcessModel.h new file mode 100644 index 000000000..c24fd36ae --- /dev/null +++ b/src/constitutive/TACSGaussianProcessModel.h @@ -0,0 +1,287 @@ +/* +======================================================================== +Gaussian Process Model for TACS Buckling Constraints +======================================================================== +@File : TACSGaussianProcessModel.h +@Date : 2024/05/10 +@Author : Sean Phillip Engelstad +@Description : Use Gaussian Processes for machine learning techniques to +interpolate learned buckling constraints on a training dataset to new test data +points. This approach is implemented into the +TACSGPBladeStiffenedShellConstitutive class in TACS for more physically accurate +buckling constraints of stiffened panels. +*/ + +#pragma once + +// ============================================================================= +// Extension Includes +// ============================================================================= +#include "TACSObject.h" +#include "TacsUtilities.h" + +// ============================================================================= +// Class Declaration +// ============================================================================= + +class TACSGaussianProcessModel : public TACSObject { + public: + TACSGaussianProcessModel(int n_train, int n_param, const TacsScalar Xtrain[], + const TacsScalar alpha[], const TacsScalar theta[]); + ~TACSGaussianProcessModel(); + + // predict the test data at a single point using a matrix-vector product + // this is the mean test data prediction. The offline training beforehand + // trains the mean surface of the training set to match mu - 3 * sigma, a + // bounding curve for buckling so that way we don't need to invert a + // covariance matrix each time. here Xtest is one point prediction with an + // array of length n_param + + /** + * @brief predict the mean test data Ytest for one test data point + * + * @param Xtest the 1-tensor test data inputs [param1, param2, param3, param4] + * @return the predicted scalar test data Ytest + */ + TacsScalar predictMeanTestData(const TacsScalar* Xtest); + + /** + * @brief backpropagate derivatives df/dYtest to df/dXtest for + * predictMeanTestData + * + * @param Ysens the derivative df/dYtest as a scalar + * @param Xtest the 1-tensor test data inputs [param1, param2, param3, param4] + * @return the derivative df/dXtest as a 1-tensor + */ + TacsScalar predictMeanTestDataSens(const TacsScalar Ysens, + const TacsScalar* Xtest, + TacsScalar* Xtestsens); + + // TESTING SCRIPTS + // --------------- + /** + * @brief test all subtests in the GaussianProcess model + * + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the maximum relative error among all GP subtests + */ + TacsScalar testAllGPTests(TacsScalar epsilon, int printLevel); + + /** + * @brief test the backpropagation of the predict mean test data and its sens + * routine + * + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the relative error for predictMeanTestData and its sens routine + */ + TacsScalar testPredictMeanTestData(TacsScalar epsilon, int printLevel); + + /** + * @brief test the backpropagation of the kernel() method and its sens routine + * + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the relative error for the kernel() and kernelSens derivatives + */ + virtual TacsScalar testKernelSens(TacsScalar epsilon, int printLevel) { + return 0.0; + }; + + /** + * @brief a differentiable form of the relu function + * + * @param x the input for soft_relu(x) + * @param rho the smoothing parameter rho_KS + * @return the soft_relu(x) + */ + static inline TacsScalar soft_relu(TacsScalar x, TacsScalar rho) { + TacsScalar one = 1.0; + return 1.0 / rho * log(one + exp(rho * x)); + }; + + /** + * @brief Jacobian dsoft_relu(x)/dx of the soft_relu function + * + * @param x the input for soft_relu(x) + * @param rho the smoothing parameter rho_KS + * @return the jacobian dsoft_relu(x)/dx + */ + static inline TacsScalar soft_relu_sens(TacsScalar x, TacsScalar rho) { + TacsScalar one = 1.0; + return exp(rho * x) / (one + exp(rho * x)); + }; + + /** + * @brief test the soft_relu jacobian + * + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the relative error of the soft_relu() and soft_relu_sens() jacobian + * routines compared to finite diff + */ + static TacsScalar test_soft_relu(TacsScalar epsilon) { + TacsScalar x = 1.0, + rho = 1.0; // very low rho for smoother function for deriv test + TacsScalar f0 = soft_relu(x - epsilon, rho); + TacsScalar f2 = soft_relu(x + epsilon, rho); + TacsScalar centDiff = (f2 - f0) / 2.0 / epsilon; + TacsScalar analyDeriv = soft_relu_sens(x, rho); + TacsScalar relError = (analyDeriv - centDiff) / centDiff; + relError = abs(TacsRealPart(relError)); + return relError; + }; + + /** + * @brief a differentiable form of the absolute value function + * + * @param x the input for soft_abs(x) + * @param rho the smoothing parameter rho_KS + * @return the soft_abs(x) + */ + static inline TacsScalar soft_abs(TacsScalar x, TacsScalar rho) { + return 1.0 / rho * log(exp(-rho * x) + exp(rho * x)); + }; + + /** + * @brief Jacobian dsoft_abs(x)/dx of the soft_abs function + * + * @param x the input for soft_abs(x) + * @param rho the smoothing parameter rho_KS + * @return the jacobian dsoft_abs(x)/dx + */ + static inline TacsScalar soft_abs_sens(TacsScalar x, TacsScalar rho) { + return (exp(rho * x) - exp(-rho * x)) / (exp(-rho * x) + exp(rho * x)); + }; + + /** + * @brief test the soft_abs jacobian + * + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the relative error of the soft_abs() and soft_abs_sens() jacobian + * routines compared to finite diff + */ + static TacsScalar test_soft_abs(TacsScalar epsilon) { + TacsScalar x = 1.0, + rho = 1.0; // very low rho for smoother function for deriv test + TacsScalar f0 = soft_abs(x - epsilon, rho); + TacsScalar f2 = soft_abs(x + epsilon, rho); + TacsScalar centDiff = (f2 - f0) / 2.0 / epsilon; + TacsScalar analyDeriv = soft_abs_sens(x, rho); + TacsScalar relError = (analyDeriv - centDiff) / centDiff; + relError = abs(TacsRealPart(relError)); + return relError; + }; + + // GETTERS AND SETTERS + // ------------------- + + int getNtrain() { return n_train; }; + int getNparam() { return n_param; }; + TacsScalar getKS() { return ks; }; + void setKS(TacsScalar ks) { this->ks = ks; }; + void setAlpha(TacsScalar* alpha) { this->alpha = alpha; }; + void setTheta(TacsScalar* theta) { this->theta = theta; }; + void getTrainingData(TacsScalar* Xtrain) { Xtrain = this->Xtrain; }; + void getTheta(TacsScalar* theta) { theta = this->theta; }; + + // virtual functions for the kernel definition and its sensitivity + + /** + * @brief virtual function for the kernel(x,x') for one test point and one + * training data point each + * + * @param Xtest the test data point, rank 1-tensor of length n_param + * @param Xtrain the training data point, rank 1-tensor of length n_param + * @return the kernel value k(Xtest,Xtrain) which gives correlation between + * these two points from our model + */ + virtual TacsScalar kernel(const TacsScalar* Xtest, + const TacsScalar* Xtrain) = 0; + + protected: + /** + * @brief backpropagate derivatives of the kernel function to the Xtest input + * (this is a virtual function here in the base class) + * + * @param ksens the derivative df/dkernel + * @param Xtest the test data point, rank 1-tensor of length n_param + * @param Xtrain the training data point, rank 1-tensor of length n_param + * @return the derivatives of the Xtest input df/dXtest through the kernel + */ + virtual void kernelSens(const TacsScalar ksens, const TacsScalar* Xtest, + const TacsScalar* Xtrain, TacsScalar* Xtestsens) = 0; + + int n_train; + int n_param; + int n_theta = 14; + // rank 1-tensor of length [n_param*n_train] [X] + // if each point of Xtrain has data [rho0, xi, gamma, delta, zeta] with + // n_Train=5 then the entries are basically [rho01, xi1, gamma1, delta1, + // zeta1, rho02, xi2, gamma2, delta2, zeta2, ..., zetaN] + TacsScalar* Xtrain; + TacsScalar* alpha; + TacsScalar* theta; // hyperparameters + + TacsScalar ks = 10.0; // ks setting for smooth kernel functions +}; + +class TACSBucklingGaussianProcessModel : public TACSGaussianProcessModel { + public: + TACSBucklingGaussianProcessModel(int n_train, const TacsScalar Xtrain[], + const TacsScalar alpha[], + const TacsScalar theta[]) + : TACSGaussianProcessModel(n_train, N_PARAM, Xtrain, alpha, theta){}; + ~TACSBucklingGaussianProcessModel(){}; + + /** + * @brief test the backpropagation of the kernel() method and its sens routine + * + * @param epsilon the step size for a finite difference or complex-step test + * (multiply by 1j if complex-step) + * @param printLevel an integer flag, with 0 to not print the test result to + * terminal and 1 to print to terminal + * @return the relative error for the kernel() and kernelSens derivatives + */ + TacsScalar testKernelSens(TacsScalar epsilon, int printLevel) override; + + /** + * @brief AxialGP kernel(x,x') for one test point and one training data point + * each + * + * @param Xtest the test data point, rank 1-tensor of length 4 + * @param Xtrain the training data point, rank 1-tensor of length 4 + * @return the kernel value k(Xtest,Xtrain) which gives correlation between + * these two points from our model + */ + TacsScalar kernel(const TacsScalar* Xtest, const TacsScalar* Xtrain) override; + + protected: + /** + * @brief backpropagate derivatives of the kernel function to the Xtest input + * for AxialGP + * + * @param ksens the derivative df/dkernel + * @param Xtest the test data point, rank 1-tensor of length 4 + * @param Xtrain the training data point, rank 1-tensor of length 4 + * @return the derivatives of the Xtest input df/dXtest through the kernel + */ + void kernelSens(const TacsScalar ksens, const TacsScalar* Xtest, + const TacsScalar* Xtrain, TacsScalar* Xtestsens) override; + + // there are 4 parameters [log(xi), log(rho_0), log(1+gamma), log(zeta)] for + // the axial model + static const int N_PARAM = 4; +}; \ No newline at end of file diff --git a/src/constitutive/TACSPanelGPs.cpp b/src/constitutive/TACSPanelGPs.cpp new file mode 100644 index 000000000..65273acb9 --- /dev/null +++ b/src/constitutive/TACSPanelGPs.cpp @@ -0,0 +1,124 @@ +#include "TACSPanelGPs.h" + +TACSPanelGPs::TACSPanelGPs(TACSBucklingGaussianProcessModel* axialGP, + TACSBucklingGaussianProcessModel* shearGP, + TACSBucklingGaussianProcessModel* cripplingGP, + bool saveData) { + this->axialGP = axialGP; + if (this->axialGP) { + this->axialGP->incref(); + } + this->shearGP = shearGP; + if (this->shearGP) { + this->shearGP->incref(); + } + this->cripplingGP = cripplingGP; + if (this->cripplingGP) { + this->cripplingGP->incref(); + } + + // initialize all the saved data to zeros and the right length + savedForward = new bool[n_save]; + savedYtest = new TacsScalar[n_save]; + savedAdjoint = new bool[n_save]; + savedJacobians = new TacsScalar[n_save_adj]; + + this->saveData = saveData; +} + +TACSPanelGPs::~TACSPanelGPs() { + // destructor for the base TACSGaussianProcessModel class + // destroy the gaussian process model objects if they exist + if (this->axialGP) { + // this object is shared, may not want to delete it (unless a local copy is + // made) + this->axialGP->decref(); + this->axialGP = nullptr; + } + + if (this->shearGP) { + // this object is shared, may not want to delete it (unless a local copy is + // made) + this->shearGP->decref(); + this->shearGP = nullptr; + } + + if (this->cripplingGP) { + // this object is shared, may not want to delete it (unless a local copy is + // made) + this->cripplingGP->decref(); + this->cripplingGP = nullptr; + } + + // free pointers for saved data + delete[] savedForward; + delete[] savedYtest; + delete[] savedAdjoint; + delete[] savedJacobians; +} + +void TACSPanelGPs::resetSavedData() { + // goal here is to reset the saved data + memset(savedYtest, 0.0, n_save * sizeof(TacsScalar)); + memset(savedJacobians, 0.0, n_save_adj * sizeof(TacsScalar)); + for (int i = 0; i < n_save; i++) { + savedForward[i] = false; + savedAdjoint[i] = false; + } +} + +TacsScalar TACSPanelGPs::predictMeanTestData(int predInd, + const TacsScalar* Xtest) { + // assume checking for input calling is in the other class for now + // otherwise I would return garbage values.. + if (!savedForward[predInd] || !saveData) { + // axial global or local options + if (predInd == 0 || predInd == 1) { + savedYtest[predInd] = this->axialGP->predictMeanTestData(Xtest); + } + // shear global or local options + if (predInd == 2 || predInd == 3) { + savedYtest[predInd] = this->shearGP->predictMeanTestData(Xtest); + } + // crippling GP + if (predInd == 4) { + savedYtest[predInd] = this->cripplingGP->predictMeanTestData(Xtest); + } + savedForward[predInd] = true; + } + return savedYtest[predInd]; +} + +void TACSPanelGPs::predictMeanTestDataSens(int predInd, const TacsScalar Ysens, + const TacsScalar* Xtest, + TacsScalar* Xtestsens) { + // assume checking for input calling is in the other class for now + // otherwise I would return garbage values.. + + // just save the jacobians in each cell as the failure input derivatives + // for backpropagation may not be the same + + // change this part with 4* to not be hardcoded later.. + TacsScalar* localJacobian = &savedJacobians[4 * predInd]; + + if (!savedAdjoint[predInd] || !saveData) { + // axial global or local options + if (predInd == 0 || predInd == 1) { + this->axialGP->predictMeanTestDataSens(1.0, Xtest, localJacobian); + } + // shear global or local options + if (predInd == 2 || predInd == 3) { + this->shearGP->predictMeanTestDataSens(1.0, Xtest, localJacobian); + } + // crippling GP + if (predInd == 4) { + this->cripplingGP->predictMeanTestDataSens(1.0, Xtest, localJacobian); + } + savedAdjoint[predInd] = true; + } + + // now multiply the Ysens backpropagated derivative by the saved jacobian + for (int i = 0; i < 4; i++) { + Xtestsens[i] = Ysens * localJacobian[i]; + } +} diff --git a/src/constitutive/TACSPanelGPs.h b/src/constitutive/TACSPanelGPs.h new file mode 100644 index 000000000..132594c00 --- /dev/null +++ b/src/constitutive/TACSPanelGPs.h @@ -0,0 +1,124 @@ +/* +======================================================================== +Gaussian Process Model for TACS Buckling Constraints +======================================================================== +@File : TACSPanelGPs.h +@Date : 2024/05/23 +@Author : Sean Phillip Engelstad +@Description : Container for the Axial, Shear, and Crippling GPs for the +GPBladeShellConstitutive class. There is one of these container objects per TACS +component (usually corresponds to each panel in the structure). The container +objects compute the GP test values once for forward and adjoint and then return +them again for each constitutive object within a TACS component (for huge +speedup in runtime). +*/ + +#pragma once + +// ============================================================================= +// Extension Includes +// ============================================================================= +#include "TACSGaussianProcessModel.h" +#include "TACSObject.h" +#include "TacsUtilities.h" + +// ============================================================================= +// Class Declaration +// ============================================================================= + +class TACSPanelGPs : public TACSObject { + public: + /** + * TACSPanelGPs is a container object for the AxialGP, ShearGP, and + * CripplingGP GaussianProcess ML models which saves and restores the buckling + * predictions of each GP model for faster computation. Each GP model can be + * null or not. If all GP models are null or not provided, then the + * TACSGPBladeStiffenedShellConstitutive class uses closed-form buckling + * predictions instead. + * + * @param TACSBucklingGaussianProcessModel an axial gaussian process model (if + * null closed-form solution is used) + * @param TACSBucklingGaussianProcessModel a shear gaussian process model (if + * null closed-form solution is used) + * @param TACSBucklingGaussianProcessModel a crippling gaussian process model + * (if null closed-form solution is used) buckling constraints height + * @param saveData a boolean flag for whether to save and restore data or not. + */ + /* + + */ + TACSPanelGPs(TACSBucklingGaussianProcessModel* axialGP, + TACSBucklingGaussianProcessModel* shearGP, + TACSBucklingGaussianProcessModel* cripplingGP, bool saveData); + ~TACSPanelGPs(); + + /** + * predict the mean test data Ytest for one test data point + * using the GP models. The inputs are saved and restored in this method + * for each kind of buckling prediction and selecting the appropriate GP for + * each. + * // 0 - axial global, 1 - axial local, + * // 2 - shear global, 3 - shear local + * // 4 - crippling + * if the buckling predictions aren't saved yet, the computation is performed + * and the saved data and flags are updated. + * + * @param predInd the index (see above) for which buckling prediction to make + * @param Xtest the test data point, a rank 1-tensor of length 4 + * @return the Ytest mean prediction of the GP + */ + TacsScalar predictMeanTestData(int predInd, const TacsScalar* Xtest); + + /** + * derivatives of predictMeanTestData, which also saves and restores the + * jacobians for faster derivative computations across an entire panel / TACS + * component. + * + * + * @param predInd the index (see above) for which buckling prediction to make + * @param Ysens the derivative df/dYtest + * @param Xtest the test data point, a rank 1-tensor of length 4 + * @return the derivative df/dXtest which we compute by jacobian product + */ + void predictMeanTestDataSens(int predInd, const TacsScalar Ysens, + const TacsScalar* Xtest, TacsScalar* Xtestsens); + + /** + * clear and reset all the saved data. + * this also turns off the flags saying we have saved the data + * and will trigger a new computation the next time the buckling predictions + * are called. + * + * this is important in the constitutive model to ensure that the next time + * DVs are updated or we have a new forward / adjoint analysis, we make new + * buckling predictions and reset the saved data. + */ + void resetSavedData(); + + // GETTERS AND SETTERS + // ------------------- + + TACSBucklingGaussianProcessModel* getAxialGP() { return this->axialGP; } + TACSBucklingGaussianProcessModel* getShearGP() { return this->shearGP; } + TACSBucklingGaussianProcessModel* getCripplingGP() { + return this->cripplingGP; + } + + protected: + const int n_save = 5; + int n_save_adj = 20; // 5 * n_params rn + bool saveData; + + // saved forward data in this class + TacsScalar* savedYtest; + bool* savedForward; + + // saved adjoint data in this class + TacsScalar* savedJacobians; + bool* savedAdjoint; + + // stored GP model pointers + TACSBucklingGaussianProcessModel* axialGP; + TACSBucklingGaussianProcessModel* shearGP; + TACSBucklingGaussianProcessModel* cripplingGP; +}; diff --git a/src/elements/shell/TACSShellElement.h b/src/elements/shell/TACSShellElement.h index 51bfd99b1..c1e16d5d1 100644 --- a/src/elements/shell/TACSShellElement.h +++ b/src/elements/shell/TACSShellElement.h @@ -1772,4 +1772,4 @@ int TacsTestShellTyingStrain(double dh = 1e-7, int test_print_level = 2, return fail; } -#endif // TACS_SHELL_ELEMENT_H +#endif // TACS_SHELL_ELEMENT_H \ No newline at end of file diff --git a/tacs/TACS.pyx b/tacs/TACS.pyx index 81d907f7e..4113c6ec4 100644 --- a/tacs/TACS.pyx +++ b/tacs/TACS.pyx @@ -1613,7 +1613,7 @@ cdef class Assembler: return - def getAverageStresses(self): + def getAverageStresses(self, int compNum): cdef Element elem cdef ElementType elem_type cdef np.ndarray stresses @@ -1621,7 +1621,7 @@ cdef class Assembler: stresses = np.zeros((9), dtype=dtype) elem = self.getElements()[0] elem_type = elem.getElementType() - self.ptr.getAverageStresses(elem_type, stresses.data) + self.ptr.getAverageStresses(elem_type, stresses.data, compNum) return stresses def setDependentNodes(self, diff --git a/tacs/constitutive.pxd b/tacs/constitutive.pxd index 6c244ca22..94f2446e7 100644 --- a/tacs/constitutive.pxd +++ b/tacs/constitutive.pxd @@ -41,9 +41,24 @@ cdef class SolidConstitutive(Constitutive): cdef class ShellConstitutive(Constitutive): cdef TACSShellConstitutive *cptr -cdef class BladeStiffenedShellConstitutive(ShellConstitutive): +cdef class StiffenedShellConstitutive(ShellConstitutive): + cdef TACSBladeStiffenedShellConstitutive *base_ptr + +cdef class BladeStiffenedShellConstitutive(StiffenedShellConstitutive): cdef TACSBladeStiffenedShellConstitutive *blade_ptr +cdef class GPBladeStiffenedShellConstitutive(StiffenedShellConstitutive): + cdef TACSGPBladeStiffenedShellConstitutive *gp_blade_ptr + +cdef class GaussianProcess: + cdef TACSGaussianProcessModel *base_gp + +cdef class BucklingGP(GaussianProcess): + cdef TACSBucklingGaussianProcessModel *buckling_gp + +cdef class PanelGPs: + cdef TACSPanelGPs *gptr + cdef class BeamConstitutive(Constitutive): cdef TACSBeamConstitutive *cptr diff --git a/tacs/constitutive.pyx b/tacs/constitutive.pyx index e7eb94ae4..5bd0663e9 100644 --- a/tacs/constitutive.pyx +++ b/tacs/constitutive.pyx @@ -834,7 +834,188 @@ cdef class CompositeShellConstitutive(ShellConstitutive): z0=np.real(z0)) return prop -cdef class BladeStiffenedShellConstitutive(ShellConstitutive): +cdef class StiffenedShellConstitutive(ShellConstitutive): + """ + This is a base class for both the BladeStiffenedShellConstitutive and the + GPBladeStiffenedShellConstitutive classes + """ + + def setFailureModes( + self, + includePanelMaterialFailure=None, + includeStiffenerMaterialFailure=None, + includeLocalBuckling=None, + includeGlobalBuckling=None, + includeStiffenerColumnBuckling=None, + includeStiffenerCrippling=None, + ): + """ + Turn on or off each failure mode in the KS failure index computation. + If any of the entries are None, they are not modified and will be included in the failure index. + + Parameters + ---------- + includePanelMaterialFailure : bool or None + whether to include panel material / panel stress failure in the failure index + includeStiffenerMaterialFailure : bool or None + whether to include stiffener material / stiffener stress failure in the failure index + includeLocalBuckling : bool or None + whether to include local buckling (in between stiffeners) in the failure index + includeGlobalBuckling : bool or None + whether to include global buckling (modes for the full stiffened panel) in the failure index + includeStiffenerColumnBuckling : bool or None + whether to include column buckling failure in the failure index + includeStiffenerCrippling : bool or None + whether to include stiffener crippling failure in the failure index + """ + if self.base_ptr: + if includePanelMaterialFailure is not None: + self.base_ptr.setIncludePanelMaterialFailure(includePanelMaterialFailure) + if includeStiffenerMaterialFailure is not None: + self.base_ptr.setIncludeStiffenerMaterialFailure(includeStiffenerMaterialFailure) + if includeGlobalBuckling is not None: + self.base_ptr.setIncludeGlobalBuckling(includeGlobalBuckling) + if includeLocalBuckling is not None: + self.base_ptr.setIncludeLocalBuckling(includeLocalBuckling) + if includeStiffenerColumnBuckling is not None: + self.base_ptr.setIncludeStiffenerColumnBuckling(includeStiffenerColumnBuckling) + if includeStiffenerCrippling is not None: + self.base_ptr.setIncludeStiffenerCrippling(includeStiffenerCrippling) + + def setKSWeight(self, double ksWeight): + """ + Update the ks weight used for aggregating the different failure modes + + Parameters + ---------- + ksWeight : float + KS aggregation weight + """ + if self.base_ptr: + self.base_ptr.setKSWeight(ksWeight) + + def setStiffenerPitchBounds(self, TacsScalar lowerBound, TacsScalar upperBound): + """Set the lower and upper bounds for the stiffener pitch design variable + + The default bounds are 1e-3 and 1e20 + + Parameters + ---------- + lowerBound : float or complex + Lower bound + upperBound : float or complex + Upper bound + """ + if self.base_ptr: + self.base_ptr.setStiffenerPitchBounds(lowerBound, upperBound) + + def setStiffenerHeightBounds(self, TacsScalar lowerBound, TacsScalar upperBound): + """Set the lower and upper bounds for the stiffener height design variable + + The default bounds are 1e-3 and 1e20 + + Parameters + ---------- + lowerBound : float or complex + Lower bound + upperBound : float or complex + Upper bound + """ + + if self.base_ptr: + self.base_ptr.setStiffenerHeightBounds(lowerBound, upperBound) + + def setStiffenerThicknessBounds(self, TacsScalar lowerBound, TacsScalar upperBound): + """Set the lower and upper bounds for the stiffener thickness design variable + + The default bounds are 1e-4 and 1e20 + + Parameters + ---------- + lowerBound : float or complex + Lower bound + upperBound : float or complex + Upper bound + """ + + if self.base_ptr: + self.base_ptr.setStiffenerThicknessBounds(lowerBound, upperBound) + + def setPanelThicknessBounds(self, TacsScalar lowerBound, TacsScalar upperBound): + """Set the lower and upper bounds for the panel thickness design variable + + The default bounds are 1e-4 and 1e20 + + Parameters + ---------- + lowerBound : float or complex + Lower bound + upperBound : float or complex + Upper bound + """ + + if self.base_ptr: + self.base_ptr.setPanelThicknessBounds(lowerBound, upperBound) + + def setStiffenerPlyFractionBounds( + self, + np.ndarray[TacsScalar, ndim=1, mode='c'] lowerBound, + np.ndarray[TacsScalar, ndim=1, mode='c'] upperBound + ): + """Set the lower and upper bounds for the stiffener ply fraction design variables + + The default bounds are 0 and 1 + + Parameters + ---------- + lowerBound : numpy.ndarray[float or complex] + Lower bound + upperBound : numpy.ndarray[float or complex] + Upper bounds + + Raises + ------ + ValueError + Raises error if the length of lowerBound or upperBound is not equal to the number of stiffener plies + """ + + if self.base_ptr: + if len(lowerBound) != self.base_ptr.getNumStiffenerPlies(): + raise ValueError('lowerBound must have length numStiffenerPlies') + if len(upperBound) != self.base_ptr.getNumStiffenerPlies(): + raise ValueError('upperBound must have length numStiffenerPlies') + self.base_ptr.setStiffenerPlyFractionBounds(lowerBound.data, upperBound.data) + + def setPanelPlyFractionBounds( + self, + np.ndarray[TacsScalar, ndim=1, mode='c'] lowerBound, + np.ndarray[TacsScalar, ndim=1, mode='c'] upperBound + ): + """Set the lower and upper bounds for the panel ply fraction design variables + + The default bounds are 0 and 1 + + Parameters + ---------- + lowerBound : numpy.ndarray[float or complex] + Lower bound + upperBound : numpy.ndarray[float or complex] + Upper bounds + + Raises + ------ + ValueError + Raises error if the length of lowerBound or upperBound is not equal to the number of panel plies + """ + + if self.base_ptr: + if len(lowerBound) != self.base_ptr.getNumPanelPlies(): + raise ValueError('lowerBound must have length numPanelPlies') + if len(upperBound) != self.base_ptr.getNumPanelPlies(): + raise ValueError('upperBound must have length numPanelPlies') + self.base_ptr.setPanelPlyFractionBounds(lowerBound.data, upperBound.data) + +cdef class BladeStiffenedShellConstitutive(StiffenedShellConstitutive): """This constitutive class models a shell stiffened with T-shaped stiffeners. The stiffeners are not explicitly modelled. Instead, their stiffness is "smeared" across the shell. @@ -937,7 +1118,7 @@ cdef class BladeStiffenedShellConstitutive(ShellConstitutive): if stiffenerPlyFracNums.dtype != np.intc: stiffenerPlyFracNums = stiffenerPlyFracNums.astype(np.intc) - self.blade_ptr = new TACSBladeStiffenedShellConstitutive( + self.base_ptr = new TACSBladeStiffenedShellConstitutive( panelPly.ptr, stiffenerPly.ptr, kcorr, @@ -961,165 +1142,667 @@ cdef class BladeStiffenedShellConstitutive(ShellConstitutive): stiffenerPlyFracNums.data, flangeFraction ) - self.ptr = self.cptr = self.blade_ptr + self.ptr = self.cptr = self.blade_ptr = self.base_ptr self.ptr.incref() - def setFailureModes( + +cdef class GaussianProcess: + """ + Base class for constructing Gaussian Process ML models to predict buckling loads. + This is an abstract base class and hence has no constructor, only methods used by its subclasses. + """ + # base class so no constructor here + def predict_mean_test_data( self, - includePanelMaterialFailure=None, - includeStiffenerMaterialFailure=None, - includeLocalBuckling=None, - includeGlobalBuckling=None, - includeStiffenerColumnBuckling=None, - includeStiffenerCrippling=None, + np.ndarray[TacsScalar, ndim=1, mode='c'] Xtest, ): + """ + This method makes buckling predictions at each point of an Xtest dataset and returns the Ytest tensor of buckling load predictions. + Note that the Xtest inputs and Ytest outputs are in log-space (more details below). - if self.blade_ptr: - if includePanelMaterialFailure is not None: - self.blade_ptr.setIncludePanelMaterialFailure(includePanelMaterialFailure) - if includeStiffenerMaterialFailure is not None: - self.blade_ptr.setIncludeStiffenerMaterialFailure(includeStiffenerMaterialFailure) - if includeGlobalBuckling is not None: - self.blade_ptr.setIncludeGlobalBuckling(includeGlobalBuckling) - if includeLocalBuckling is not None: - self.blade_ptr.setIncludeLocalBuckling(includeLocalBuckling) - if includeStiffenerColumnBuckling is not None: - self.blade_ptr.setIncludeStiffenerColumnBuckling(includeStiffenerColumnBuckling) - if includeStiffenerCrippling is not None: - self.blade_ptr.setIncludeStiffenerCrippling(includeStiffenerCrippling) + Parameters + ---------- + Xtest - np.ndarray + a rank 1 tensor of size (4*N_test) which contains the list of nondim buckling inputs + namely [log(1+xi), log(rho0), log(1+gamma), log(1+10^3 * zeta)] concatenated for each point in the test set. + specifically: [log_xi1, log_rho01, log_gamma1, log_zeta1, log_xi2, ...] + + Returns: + Ytest (np.ndarray) : a rank 1 tensor Ytest of size (N_test,) containing the log(N_ij,cr^*) aka log buckling load + outputs where N_ij,cr^* is N_11,cr^* for the axial GP, N_12,cr^* for the shear GP. + """ + return self.base_gp.predictMeanTestData(Xtest.data) - def setKSWeight(self, double ksWeight): + def getNparam(self): """ - Update the ks weight used for aggregating the different failure modes + Get the number of input parameters in the Gaussian Process model (4 for all of the current base classes) + + Returns: + nparams (int) : the number of input parameters in Xtest [4 for the current implemented classes] + """ + return self.base_gp.getNparam() + + def getNtrain(self): + """ + Get the number of input training data points used in the model training weights + + Returns: + ntrain (int) : the number of training data points in alpha_train, the training weights + """ + return self.base_gp.getNtrain() + + def getTrainingData(self): + """ + Get the training dataset Xtrain used for training the Gaussian Process ML model + + Returns: + Xtrain (np.ndarray) : a rank-1 tensor (4*N_train,) of the training dataset non-dim inputs + namely [log(1+xi), log(rho0), log(1+gamma), log(1+10^3 * zeta)] for each training point + or [log_xi1, log_rho01, log_gamma1, log_zeta1, log_xi2, ...] + """ + cdef int ntrain = self.getNtrain() + cdef int nparam = self.getNparam() + cdef np.ndarray train_data = np.zeros((ntrain*nparam,), dtype=dtype) + self.base_gp.getTrainingData(train_data.data) + return train_data + + def setKS(self, TacsScalar ksWeight, + np.ndarray[TacsScalar, ndim=1, mode='c'] Ytrain): + """ + Set the KS parameter of the Gaussian Process ML model used in the kernel functions. + Ytrain is provided since this is needed to retrain the ML model weights for the new KS input, + and Ytrain is not stored in the GP data structure. Parameters ---------- ksWeight : float - KS aggregation weight + the Kresselmeier-Steinhauser aggregation parameter rho_KS used for smooth relu and smooth abs value + in the GP kernel functions. + Ytrain : np.ndarray + training dataset log(buckling load) outputs in order to retrain the ML model, + it's a rank-1 tensor of size (Ntrain,) + """ + # print("Warning: need to retrain the weights as KS has changed\n") + self.base_gp.setKS(ksWeight) + self.recompute_alpha(Ytrain) + return + + def setTheta(self, + np.ndarray[TacsScalar, ndim=1, mode='c'] theta, + np.ndarray[TacsScalar, ndim=1, mode='c'] Ytrain): """ - if self.blade_ptr: - self.blade_ptr.setKSWeight(ksWeight) + Set the hyperparameters theta of the Gaussian Process ML model used in the kernel functions. + Ytrain is provided since this is needed to retrain the ML model weights for the new KS input, + and Ytrain is not stored in the GP data structure. - def setStiffenerPitchBounds(self, TacsScalar lowerBound, TacsScalar upperBound): - """Set the lower and upper bounds for the stiffener pitch design variable + Parameters + ---------- + theta : np.ndarray + rank 1-tensor of model hyperparameters (currently a length 13 1-tensor). + Ytrain : np.ndarray + training dataset log(buckling load) outputs in order to retrain the ML model, + it's a rank-1 tensor of size (Ntrain,) + """ + # print("Warning: need to retrain the weights as KS has changed\n") + self.base_gp.setTheta(theta.data) + self.recompute_alpha(Ytrain) + return - The default bounds are 1e-3 and 1e20 + def test_all_gp_tests(self, TacsScalar epsilon, int printLevel): + """ + run all the internal Gaussian Process ML model derivative tests Parameters ---------- - lowerBound : float or complex - Lower bound - upperBound : float or complex - Upper bound - """ - if self.blade_ptr: - self.blade_ptr.setStiffenerPitchBounds(lowerBound, upperBound) + epsilon : TacsScalar + the step size for each derivative test + printLevel : int + an input of 0 does not print each derivative test result, an input of 1 does print each test result to terminal. - def setStiffenerHeightBounds(self, TacsScalar lowerBound, TacsScalar upperBound): - """Set the lower and upper bounds for the stiffener height design variable + Returns: + max relative error (TacsScalar) : the max relative error among all internal derivative tests of the model + """ + return self.base_gp.testAllGPTests(epsilon, printLevel) - The default bounds are 1e-3 and 1e20 + def kernel( + self, + np.ndarray[TacsScalar, ndim=1, mode='c'] Xtest, + np.ndarray[TacsScalar, ndim=1, mode='c'] Xtrain, + ): + """ + call the kernel function of the Gaussian Process model on an arbitrary test point and training point pair. + this function is for debugging purposes only => to make sure the kernel functions implemented inside the TACS GP models + match those of the ml_buckling repo. Parameters ---------- - lowerBound : float or complex - Lower bound - upperBound : float or complex - Upper bound + Xtest : np.ndarray + a rank-1 tensor of size (nparams,) currently size (4,) of the log non-dimensional inputs at the test datapoint. + the inputs are [log(1+xi), log(rho0), log(1+gamma), log(1+10^3 * zeta)]. + Xtrain : np.ndarray + a rank-1 tensor of size (nparams,) currently size (4,) of the log non-dimensional inputs at the training datapoint. + the inputs are [log(1+xi), log(rho0), log(1+gamma), log(1+10^3 * zeta)]. """ + return self.base_gp.kernel(Xtest.data, Xtrain.data) - if self.blade_ptr: - self.blade_ptr.setStiffenerHeightBounds(lowerBound, upperBound) + def recompute_alpha(self, np.ndarray[TacsScalar, ndim=1, mode='c'] Ytrain): + """ + a helper function that recomputes the training weights alpha_train of size (n_train,) [a rank 1-tensor]. + this function solves the linear equation [K(X_train,X_train;theta) + sigma_n^2 * I] * alpha_train = Y_train + for the training weights alpha_train with sigma_n from the hyperparameters theta which come from inside the model. + this is called whenever the ksWeight or theta hyperparameters are changed so we update the ML model. - def setStiffenerThicknessBounds(self, TacsScalar lowerBound, TacsScalar upperBound): - """Set the lower and upper bounds for the stiffener thickness design variable + Parameters + ---------- + Ytrain : np.ndarray + training dataset log(buckling load) outputs in order to retrain the ML model, + it's a rank-1 tensor of size (Ntrain,) + """ + #return + cdef int ntrain = self.getNtrain() + cdef int nparam = self.getNparam() + cdef np.ndarray theta = np.zeros((14,), dtype=dtype) + cdef np.ndarray Xtrain = np.zeros((ntrain * nparam,), dtype=dtype) + self.base_gp.getTheta(theta.data) + self.base_gp.getTrainingData(theta.data) + cdef TacsScalar sigma_n = theta[-1] + + # make the training matrix + cdef np.ndarray[TacsScalar, ndim=2, mode='c'] kernel_matrix = np.array([[ + self.kernel(Xtrain[i:(i+nparam)], Xtrain[j:(j+nparam)]) + sigma_n**2 for i in range(ntrain) + ] for j in range(ntrain)]) + + # recompute the training weights + cdef np.ndarray[TacsScalar, ndim=1, mode='c'] alpha = np.linalg.solve(kernel_matrix, Ytrain) + self.base_gp.setAlpha(alpha.data) + return + +cdef class BucklingGP(GaussianProcess): + """ + Gaussian Process ML model to predict N_11,cr^* non-dimensional buckling loads of global axial modes. + Local axial mode predictions can also be made with gamma = 0 and xi, rho0 computed for the local panel. + The ML model uses non-dimensional inputs and outputs as follows: + log(N_11,cr^*) = my_axial_GP.predict_mean_test_data(log(1+xi), log(rho0), log(1+gamma), log(1+10^3 * zeta)) + log(N_12,cr^*) = my_shear_GP.predict_mean_test_data(log(1+xi), log(rho0), log(1+gamma), log(1+10^3 * zeta)) + The axialGP model accomodoates making multiple test point predictions in parallel as do many ML models. - The default bounds are 1e-4 and 1e20 + Parameters + ---------- + Xtrain : np.ndarray + a rank-1 tensor (4*N_train,) of the training dataset non-dim inputs + namely [log(1+xi), log(rho0), log(1+gamma), log(1+10^3 * zeta)] for each training point + or [log_xi1, log_rho01, log_gamma1, log_zeta1, log_xi2, ...] + alpha : np.ndarray + a rank-1 tensor (N_train,) containing the training weights for the ML model (computed by python training in ml_buckling repo) + theta : np.ndarray + a rank-1 tensor (13,) containing each of the model hyperparameters in the kernel function + """ + n_param = 4 # [log(1+xi), log(rho0), log(1+gamma), log(1+10^3 * zeta)] + + @classmethod + def from_csv(cls, csv_file, theta_csv): + """ + Construct an BucklingGP from a csv_files in the ml_buckling repo (or your own dataset csv files) which contain + the training weights of the ML model and the optimal hyperparameters theta. + This is the method commonly used to construct the BucklingGP. Namely using the ml_buckling + The common construction of this class from the ml_buckling repo, https://github.com/smdogroup/ml_buckling, + and is the following: + axial_gp = BucklingGP.from_csv( + csv_file=mlb.axialGP_csv, theta_csv=mlb.axial_theta_csv + ) + shear_gp = BucklingGP.from_csv( + csv_file=mlb.shearGP_csv, theta_csv=mlb.shear_theta_csv + ) Parameters ---------- - lowerBound : float or complex - Lower bound - upperBound : float or complex - Upper bound + csv_file : filepath or str + path to a csv_file that holds the Xtrain training data with 5 columns for each entry of Xtrain + namely [log(1+xi), log(rho0), log(1+gamma), log(1+10^3 * zeta), alphaTrain]. + theta_csv : filepath or str + path to a csv file that holds the theta hyperparameters in one column with 13 entries. + + Returns: + BucklingGP object """ + # need csv with five columns: [Xparam1, Xparam2, Xparam3, Xparam4, alpha] + # optional import - if self.blade_ptr: - self.blade_ptr.setStiffenerThicknessBounds(lowerBound, upperBound) + full_arr = np.loadtxt(csv_file, skiprows=1, delimiter=",") + Xtrain_mat = full_arr[:, 1:5].astype(dtype=dtype) + alpha = full_arr[:, -1].astype(dtype=dtype) - def setPanelThicknessBounds(self, TacsScalar lowerBound, TacsScalar upperBound): - """Set the lower and upper bounds for the panel thickness design variable + n_train = Xtrain_mat.shape[0] + n_param = 4 - The default bounds are 1e-4 and 1e20 + Xtrain = np.zeros((n_train*n_param,), dtype=dtype) + # stagger the array entries + for iparam in range(4): + Xtrain[iparam::4] = Xtrain_mat[:,iparam] + + theta_opt = np.loadtxt(theta_csv, skiprows=1, delimiter=",")[:,-1].astype(dtype=dtype) + + return cls(n_train, Xtrain, alpha, theta_opt) + + def __cinit__( + self, + int n_train, + np.ndarray[TacsScalar, ndim=1, mode='c'] Xtrain, + np.ndarray[TacsScalar, ndim=1, mode='c'] alpha, + np.ndarray[TacsScalar, ndim=1, mode='c'] theta, + ): + self.buckling_gp = new TACSBucklingGaussianProcessModel( + n_train, + Xtrain.data, + alpha.data, + theta.data, + ) + self.base_gp = self.buckling_gp + +cdef class PanelGPs: + """ + This object holds the individual GP objects for a single panel. One PanelGP object is shared among the constitutive + object for each panel, and the input and output buckling loads and derivatives are saved and restored so that + only one call to each GP object is required per panel (speeds up computation time). + The construction of the TACS callback with the panelGPs is such that only one PanelGPs object is made per TACSComponent + (assuming each TACSComponent is associated with a different panel). + + The typical construction from an example in ml_buckling repo (in file 4_aob_opt/_gp_callback) is: + # now build a dictionary of PanelGP objects which manage the GP for each tacs component/panel + + def callback_generator(tacs_component_names): + axialGP = constitutive.BucklingGP.from_csv( csv_file=mlb.axialGP_csv, theta_csv=mlb.axial_theta_csv ) + shearGP = constitutive.BucklingGP.from_csv( csv_file=mlb.shearGP_csv, theta_csv=mlb.shear_theta_csv ) + panelGP_dict = constitutive.PanelGPs.component_dict( tacs_component_names, axialGP=axialGP, shearGP=shearGP ) + + def gp_callback(dvNum, compID, compDescript, elemDescripts, specialDVs, **kwargs): + + # get the panelGPs object associated with this tacs component + panelGPs = panelGP_dict[compDescript] + + # ... (after this you build the TACSMaterialProperties objects and TACSGPBladeConstitutive objects. + + Parameters + ---------- + BucklingGP : TACSAxialGaussianProcessModel, optional + GP model for axial buckling data, if None uses closed-form instead + BucklingGP : TACSShearGaussianProcessModel, optional + GP model for shear buckling data, if None uses closed-form instead + BucklingGP : TACSCripplingGaussianProcessModel, optional + GP model for crippling buckling data, if None uses closed-form instead + saveData : bool + whether to save and restore input and output buckling predictions for more efficient buckling predictions (default True) + """ + def __cinit__( + self, + BucklingGP axialGP = None, + BucklingGP shearGP = None, + BucklingGP cripplingGP = None, + bool saveData = True, + ): + # make null ptrs for GPs if not defined and store them in this class too + cdef TACSBucklingGaussianProcessModel *axial_gp_ptr = NULL + if axialGP is not None: + axial_gp_ptr = (axialGP).buckling_gp + cdef TACSBucklingGaussianProcessModel *shear_gp_ptr = NULL + if shearGP is not None: + shear_gp_ptr = (shearGP).buckling_gp + cdef TACSBucklingGaussianProcessModel *crippling_gp_ptr = NULL + if cripplingGP is not None: + crippling_gp_ptr = (cripplingGP).buckling_gp + + self.gptr = new TACSPanelGPs( + axial_gp_ptr, + shear_gp_ptr, + crippling_gp_ptr, + saveData, + ) + + @classmethod + def component_dict( + cls, + tacs_components, # list of strings of each tacs component name + BucklingGP axialGP = None, + BucklingGP shearGP = None, + BucklingGP cripplingGP = None, + bool saveData = True, + ): + """ + constructs a dictionary of PanelGPs objects, one for each tacs component. The dictionary is of the form: + { tacs_component (str) : PanelGPs object } Parameters ---------- - lowerBound : float or complex - Lower bound - upperBound : float or complex - Upper bound + BucklingGP : TACSAxialGaussianProcessModel, optional + GP model for axial buckling data, if None uses closed-form instead + BucklingGP : TACSShearGaussianProcessModel, optional + GP model for shear buckling data, if None uses closed-form instead + BucklingGP : TACSCripplingGaussianProcessModel, optional + GP model for crippling buckling data, if None uses closed-form instead + saveData : bool + whether to save and restore input and output buckling predictions for more efficient buckling predictions (default True) """ + _dict = {} + for comp_name in tacs_components: + _dict[comp_name] = cls( + axialGP, + shearGP, + cripplingGP, + saveData + ) + return _dict + +cdef class GPBladeStiffenedShellConstitutive(StiffenedShellConstitutive): + """" + This constitutive class models a shell stiffened with T-shaped stiffeners. + The stiffeners are not explicitly modelled. Instead, their stiffness is "smeared" across the shell. + + This class is a subclass of the BladeStiffenedShellConstitutive base class + and includes higher-fidelity buckling constraints using Gaussian Processes for Machine Learning. + The trained ML models are located on the repo, https://github.com/smdogroup/ml_buckling. + + For the methods below, consider a panel with dimensions a the panel length, b the panel width, + h the panel thickness, and stiffeners along the length or 1-direction. + The Dij for axial and shear modes of the panel use the panel laminate design, with the centroid shifted towards the stiffener + only for the D11 stiffness for the global modes (the local modes use D11 at the center of the skin). + The stiffener has a lateral spacing s_p the stiffener pitch, stiffener height h_s, stiffener thickness t_s. + For more information on this constitutive class, see our paper https://arc.aiaa.org/doi/abs/10.2514/6.2024-3981, + "Machine Learning to Improve Buckling Predictions for Efficient Structural Optimization of Aircraft Wings" + by Sean Engelstad, Brian Burke, Graeme Kennedy. - if self.blade_ptr: - self.blade_ptr.setPanelThicknessBounds(lowerBound, upperBound) + Parameters + ---------- + panelPly : tacs.constitutive.OrthotropicPly + Ply model to use for the panel + stiffenerPly : tacs.constitutive.OrthotropicPly + Ply model to use for the stiffener + panelLength : float or complex + Panel length DV value + stiffenerPitch : float or complex + Stiffener pitch DV value + panelThick : float or complex + Panel thickness DV value + panelPlyAngles : numpy.ndarray[float or complex] + Array of ply angles in the panel + panelPlyFracs : numpy.ndarray[float or complex] + Array of ply fractions in the panel + stiffenerHeight : float or complex + Stiffener height DV value + stiffenerThick : float or complex + Stiffener thickness DV value + stiffenerPlyAngles : numpy.ndarray[float or complex] + Array of ply angles for the stiffener + stiffenerPlyFracs : numpy.ndarray[float or complex] + Array of ply fractions for the stiffener + panelWidth : float or complex + Panel width DV value + kcorr : float or complex, optional + Shear correction factor, defaults to 5.0/6.0 + flangeFraction : float, optional + Ratio of the stiffener base width to the stiffener height. Defaults to 1.0 + panelLengthNum : int, optional + Panel lenth DV number, passing a negative value tells TACS not to treat this as a DV. Defaults to -1 + stiffenerPitchNum : int, optional + Stiffener pitch DV number, passing a negative value tells TACS not to treat this as a DV. Defaults to -1 + panelThickNum : int, optional + Panel thickness DV number, passing a negative value tells TACS not to treat this as a DV. Defaults to -1 + panelPlyFracNums : numpy.ndarray[np.intc], optional + Array of ply fraction DV numbers in the panel, passing negative values tells TACS not to treat that ply fraction as a DV. Defaults to -1's + stiffenerHeightNum : int, optional + Stiffener height DV number, passing a negative value tells TACS not to treat this as a DV. Defaults to -1 + stiffenerThickNum : int, optional + Stiffener thickness DV number, passing a negative value tells TACS not to treat this as a DV. Defaults to -1 + stiffenerPlyFracNums : numpy.ndarray[numpy.intc], optional + Array of ply fraction DV numbers for the stiffener, passing negative values tells TACS not to treat that ply fraction as a DV. Defaults to -1's + panelWidthNum : int, optional + Panel width DV number, passing a negative value tells TACS not to treat this as a DV. Defaults to -1 + CPTstiffenerCrippling : bool + whether to use CPT (classical plate theory) for stiffener crippling buckling predictions in the closed-form solution case (default False) + panelGPs: TACSPanelGPs + container for the three GP models for each panel / TACS component - def setStiffenerPlyFractionBounds( - self, - np.ndarray[TacsScalar, ndim=1, mode='c'] lowerBound, - np.ndarray[TacsScalar, ndim=1, mode='c'] upperBound + Raises + ------ + ValueError + Raises error if panelPlyAngles, panelPlyFracs, or panelPlyFracNums do not have same number of entries + ValueError + Raises error if stiffenerPlyAngles, stiffenerPlyFracs, or stiffenerPlyFracNums do not have same number of entries + """ + + def __cinit__( + self, + OrthotropicPly panelPly, + OrthotropicPly stiffenerPly, + TacsScalar panelLength, + TacsScalar stiffenerPitch, + TacsScalar panelThick, + np.ndarray[TacsScalar, ndim=1, mode='c'] panelPlyAngles, + np.ndarray[TacsScalar, ndim=1, mode='c'] panelPlyFracs, + TacsScalar stiffenerHeight, + TacsScalar stiffenerThick, + np.ndarray[TacsScalar, ndim=1, mode='c'] stiffenerPlyAngles, + np.ndarray[TacsScalar, ndim=1, mode='c'] stiffenerPlyFracs, + TacsScalar panelWidth, + TacsScalar kcorr = 5.0/6.0, + TacsScalar flangeFraction = 1.0, + int panelLengthNum = -1, + int stiffenerPitchNum = -1, + int panelThickNum = -1, + np.ndarray[int, ndim=1, mode='c'] panelPlyFracNums = None, + int stiffenerHeightNum = -1, + int stiffenerThickNum = -1, + np.ndarray[int, ndim=1, mode='c'] stiffenerPlyFracNums = None, + int panelWidthNum = -1, + bool CPTstiffenerCrippling = False, + PanelGPs panelGPs = None, ): - """Set the lower and upper bounds for the stiffener ply fraction design variables - The default bounds are 0 and 1 + # same input checks as superclass BladeStiffenedShellConstitutive + numPanelPlies = len(panelPlyAngles) + numStiffenerPlies = len(stiffenerPlyAngles) + + if panelPlyFracNums is None: + panelPlyFracNums = -np.ones([numPanelPlies], dtype=np.intc) + if stiffenerPlyFracNums is None: + stiffenerPlyFracNums = -np.ones([numStiffenerPlies], dtype=np.intc) + + if len(panelPlyFracs) != numPanelPlies: + raise ValueError('panelPlyFracs must have same length as panelPlyAngles') + if len(panelPlyFracNums) != numPanelPlies: + raise ValueError('panelPlyFracNums must have same length as panelPlyAngles') + if len(stiffenerPlyFracs) != numStiffenerPlies: + raise ValueError('stiffenerPlyFracs must have same length as stiffenerPlyAngles') + if len(stiffenerPlyFracNums) != numStiffenerPlies: + raise ValueError('stiffenerPlyFracNums must have same length as stiffenerPlyAngles') + + cdef TACSPanelGPs *panel_gp_ptr = NULL + if panelGPs is not None: + panel_gp_ptr = (panelGPs).gptr + + # Numpy's default int type is int64, but this is interpreted by Cython as a long. + if panelPlyFracNums.dtype != np.intc: + panelPlyFracNums = panelPlyFracNums.astype(np.intc) + if stiffenerPlyFracNums.dtype != np.intc: + stiffenerPlyFracNums = stiffenerPlyFracNums.astype(np.intc) + + self.base_ptr = new TACSGPBladeStiffenedShellConstitutive( + panelPly.ptr, + stiffenerPly.ptr, + kcorr, + panelLength, + panelLengthNum, + stiffenerPitch, + stiffenerPitchNum, + panelThick, + panelThickNum, + numPanelPlies, + panelPlyAngles.data, + panelPlyFracs.data, + panelPlyFracNums.data, + stiffenerHeight, + stiffenerHeightNum, + stiffenerThick, + stiffenerThickNum, + numStiffenerPlies, + stiffenerPlyAngles.data, + stiffenerPlyFracs.data, + stiffenerPlyFracNums.data, + panelWidth, + panelWidthNum, + flangeFraction, + CPTstiffenerCrippling, + panel_gp_ptr, + ) + # copy pointers to all superclasses + self.gp_blade_ptr = self.base_ptr + self.ptr = self.cptr = self.base_ptr # copy constitutive as well + self.ptr.incref() + + def nondimCriticalGlobalAxialLoad(self, TacsScalar rho_0, TacsScalar xi, TacsScalar gamma, TacsScalar zeta=0.0): + """ + predict the non-dimensional buckling load N11,cr^* for the global axial mode of a panel. + Uses the closed-form solution if PanelGPs.axialGP is None or the axialGP ML model if PanelGPs.axialGP is not None + Here D11 is for the centroid of the panel and stiffener, and the other Dij are for the panel at the panel center. Parameters ---------- - lowerBound : numpy.ndarray[float or complex] - Lower bound - upperBound : numpy.ndarray[float or complex] - Upper bounds + rho_0 : float + the affine aspect ratio of the panel, rho_0 = a/b * 4thRoot(D22 / D11) + xi : float + the laminate isotropy, xi = (D12 + 2 * D66) / sqrt(D11 * D22) + gamma : float + the stiffener stiffness ratio, gamma = E_1s * A_s / (s_p * D11) + zeta : float + the transverse shear parameters, zeta = A11 / A66 * (h/b)^2 - Raises - ------ - ValueError - Raises error if the length of lowerBound or upperBound is not equal to the number of stiffener plies + Returns: + N11,cr^* (float) : the non-dimensional output buckling load where the dimensional buckling load N11,cr is given by + N11,cr = (N11,cr^*) * pi^2 * sqrt(D11 * D22) / b^2 / (1 + delta) + with delta = E1s * As / (E1p * sp * h) """ + return self.gp_blade_ptr.nondimCriticalGlobalAxialLoad(rho_0, xi, gamma, zeta) - if self.blade_ptr: - if len(lowerBound) != self.blade_ptr.getNumStiffenerPlies(): - raise ValueError('lowerBound must have length numStiffenerPlies') - if len(upperBound) != self.blade_ptr.getNumStiffenerPlies(): - raise ValueError('upperBound must have length numStiffenerPlies') - self.blade_ptr.setStiffenerPlyFractionBounds(lowerBound.data, upperBound.data) + def nondimCriticalLocalAxialLoad(self, TacsScalar rho_0, TacsScalar xi, TacsScalar zeta=0.0): + """ + predict the non-dimensional buckling load N11,cr^* for the local axial mode of a panel. + Uses the closed-form solution if PanelGPs.axialGP is None or the axialGP ML model if PanelGPs.axialGP is not None + Here D11 is for the center of the skin, and the other Dij are for the panel at the panel center. - def setPanelPlyFractionBounds( - self, - np.ndarray[TacsScalar, ndim=1, mode='c'] lowerBound, - np.ndarray[TacsScalar, ndim=1, mode='c'] upperBound - ): - """Set the lower and upper bounds for the panel ply fraction design variables + Parameters + ---------- + rho_0 : float + the affine aspect ratio of the panel, rho_0 = a/s_p * 4thRoot(D22 / D11) + xi : float + the laminate isotropy, xi = (D12 + 2 * D66) / sqrt(D11 * D22) + zeta : float + the transverse shear parameters, zeta = A11 / A66 * (h/b)^2 - The default bounds are 0 and 1 + Returns: + N11,cr^* (float) : the non-dimensional output buckling load where the dimensional buckling load N11,cr is given by + N11,cr = (N11,cr^*) * pi^2 * sqrt(D11 * D22) / s_p^2 + """ + return self.gp_blade_ptr.nondimCriticalLocalAxialLoad(rho_0, xi, zeta) + + def nondimCriticalGlobalShearLoad(self, TacsScalar rho_0, TacsScalar xi, TacsScalar gamma, TacsScalar zeta=0.0): + """ + predict the non-dimensional buckling load N12,cr^* for the global shear mode of a panel. + Uses the closed-form solution if PanelGPs.shearGP is None or the shearGP ML model if PanelGPs.shearGP is not None. + Here D11 is for the centroid of the panel and stiffener, and the other Dij are for the panel at the panel center. Parameters ---------- - lowerBound : numpy.ndarray[float or complex] - Lower bound - upperBound : numpy.ndarray[float or complex] - Upper bounds + rho_0 : float + the affine aspect ratio of the panel, rho_0 = a/b * 4thRoot(D22 / D11) + xi : float + the laminate isotropy, xi = (D12 + 2 * D66) / sqrt(D11 * D22) + gamma : float + the stiffener stiffness ratio, gamma = E_1s * A_s / (s_p * D11) + zeta : float + the transverse shear parameters, zeta = A11 / A66 * (h/b)^2 - Raises - ------ - ValueError - Raises error if the length of lowerBound or upperBound is not equal to the number of panel plies + Returns: + N12,cr^* (float) : the non-dimensional output buckling load where the dimensional buckling load N12,cr is given by + N12,cr = (N12,cr^*) * pi^2 * 4thRoot(D11 * D22^3) / b^2 """ + return self.gp_blade_ptr.nondimCriticalGlobalShearLoad(rho_0, xi, gamma, zeta) - if self.blade_ptr: - if len(lowerBound) != self.blade_ptr.getNumPanelPlies(): - raise ValueError('lowerBound must have length numPanelPlies') - if len(upperBound) != self.blade_ptr.getNumPanelPlies(): - raise ValueError('upperBound must have length numPanelPlies') - self.blade_ptr.setPanelPlyFractionBounds(lowerBound.data, upperBound.data) + def nondimCriticalLocalShearLoad(self, TacsScalar rho_0, TacsScalar xi, TacsScalar zeta=0.0): + """ + predict the non-dimensional buckling load N12,cr^* for the local shear mode of a panel. + Uses the closed-form solution if PanelGPs.shearGP is None or the shearGP ML model if PanelGPs.shearGP is not None. + D11 here is for the center of the panel, and the other Dij are for the panel at the panel center. + + Parameters + ---------- + rho_0 : float + the affine aspect ratio of the panel, rho_0 = a/s_p * 4thRoot(D22 / D11) + xi : float + the laminate isotropy, xi = (D12 + 2 * D66) / sqrt(D11 * D22) + zeta : float + the transverse shear parameters, zeta = A11 / A66 * (h/b)^2 + + Returns: + N12,cr^* (float) : the non-dimensional output buckling load where the dimensional buckling load N12,cr is given by + N12,cr = (N12,cr^*) * pi^2 * 4thRoot(D11 * D22^3) / s_p^2 + """ + return self.gp_blade_ptr.nondimCriticalLocalShearLoad(rho_0, xi, zeta) + + def nondimStiffenerCripplingLoad(self, TacsScalar rho_0, TacsScalar xi, TacsScalar genPoiss, TacsScalar zeta=0.0): + """ + predict the non-dimensional buckling load N11,cr^* for the stiffener crippling mode of the stiffener. + Uses the closed-form solution if PanelGPs.cripplingGP is None or the cripplingGP ML model if PanelGPs.cripplingGP is not None + + + Parameters + ---------- + rho_0 : float + the affine aspect ratio of the stiffener, rho_0 = a/b * 4thRoot(D22 / D11) + xi : float + the laminate isotropy, xi = (D12 + 2 * D66) / sqrt(D11 * D22) + genPoiss : float + the generalized Poisson's ratio for weak BCs such as the stiffener with its free edge, eps = D12 / (D12 + 2 * D66) + zeta : float + the transverse shear parameters, zeta = A11 / A66 * (h/b)^2 + + Returns: + N11,cr^* (float) : the non-dimensional output buckling load where the dimensional buckling load N11,cr is given by + N11,cr = (N11,cr^*) * pi^2 * sqrt(D11s * D22s) / h_s^2; here D11s, D22s are for the stiffener laminate + """ + return self.gp_blade_ptr.nondimStiffenerCripplingLoad(rho_0, xi, genPoiss, zeta) + + def setCPTstiffenerCrippling(self, bool CPTcripplingMode): + """ + choose whether to use stiffener crippling by: + CPT solution from Sean's paper if CPTcripplingMode = True + DOD experimental solution from Ali's superclass if CPTcripplingMode = False + + Parameters + ---------- + CPTcripplingMode : bool + whether to use CPT crippling solution or not + """ + if self.gp_blade_ptr: + self.gp_blade_ptr.setCPTstiffenerCrippling(CPTcripplingMode) + + def setWriteDVMode(self, int newMode): + """ + Set mode for writing DV inputs to the .f5 files + 0 - write DVs, 1 - write nondim params, 2 - write failure indexes + this is a useful tool to investigate and debug final designs. + + Parameters + ---------- + newMode: int + new mode input for the writeDVMode + """ + if self.gp_blade_ptr: + self.gp_blade_ptr.setWriteDVMode(newMode) + + def test_all_derivative_tests(self, TacsScalar epsilon, int printLevel): + """ + test all the internal derivative tests + """ + return self.gp_blade_ptr.testAllTests(epsilon, printLevel) cdef class SmearedCompositeShellConstitutive(ShellConstitutive): """ diff --git a/tacs/constraints/__init__.py b/tacs/constraints/__init__.py index 6346f1624..03a167032 100644 --- a/tacs/constraints/__init__.py +++ b/tacs/constraints/__init__.py @@ -1,6 +1,7 @@ from .adjacency import AdjacencyConstraint from .dv import DVConstraint from .panel_length import PanelLengthConstraint +from .panel_width import PanelWidthConstraint from .volume import VolumeConstraint from .base import TACSConstraint diff --git a/tacs/constraints/panel_length.py b/tacs/constraints/panel_length.py index d503af249..4fa1316f4 100644 --- a/tacs/constraints/panel_length.py +++ b/tacs/constraints/panel_length.py @@ -235,6 +235,15 @@ def setDesignVars(self, x): for key in self.constraintsUpToDate: self.constraintsUpToDate[key] = False + def externalClearUpToDate(self): + """ + clear UpToDate by FUNtoFEM which sets variables into + TACS through a different interface + """ + for key in self.constraintsUpToDate: + self.constraintsUpToDate[key] = False + return + def setNodes(self, Xpts): """ Set the mesh coordinates of the structure. @@ -250,6 +259,33 @@ def setNodes(self, Xpts): self.constraintsUpToDate[key] = False self.constraintsSensUpToDate[key] = False + def computeRefAxis(self, refAxis: np.ndarray, comp_bndry_node_coords: np.ndarray): + """ + remove the panelNormal from the refAxis + + Parameters + ---------- + refAxis : numpy.ndarray + an array of size (3,) for the xyz coordinates of the original refAxis from transform object + comp_bndry_node_coords : numpy.ndarray + an array of size (3*N,) for the boundary nodal coordinates on the current panel / current TACS component + + Returns + ------- + numpy.ndarray + an array of size (3,) for xyz coords of the panel length axis after the panelNormal component has been removed + """ + # For a more accurate length calculation, roject the ref axis + # onto the "average" plane of the baseline panel geometry by + # using an SVD to compute a normal vector + centroid = np.mean(comp_bndry_node_coords, axis=0, keepdims=True) + centredPoints = comp_bndry_node_coords - centroid + _, _, VT = np.linalg.svd(centredPoints, full_matrices=False) + panelNormal = VT[-1] + refAxis -= np.dot(refAxis, panelNormal) * panelNormal + refAxis /= np.linalg.norm(refAxis) + return refAxis + def addConstraint(self, conName, compIDs=None, lower=None, upper=None, dvIndex=0): """ Generic method to adding a new constraint set for TACS. @@ -316,15 +352,8 @@ def addConstraint(self, conName, compIDs=None, lower=None, upper=None, dvIndex=0 f"The elements in component {self.meshLoader.compDescripts[compID]} do not have a reference axis. Please define one by using the 'ShellRefAxisTransform' class with your elements" ) from e - # For a more accurate length calculation, roject the ref axis - # onto the "average" plane of the baseline panel geometry by - # using an SVD to compute a normal vector - centroid = np.mean(boundaryNodeCoords[compID], axis=0, keepdims=True) - centredPoints = boundaryNodeCoords[compID] - centroid - _, _, VT = np.linalg.svd(centredPoints, full_matrices=False) - panelNormal = VT[-1] - refAxis -= np.dot(refAxis, panelNormal) * panelNormal - refAxis /= np.linalg.norm(refAxis) + # remove the panelNormal from the adjusted ref axis + refAxis = self.computeRefAxis(refAxis, boundaryNodeCoords[compID]) refAxes.append(refAxis) # Now figure out where the DV for this component lives @@ -791,6 +820,7 @@ def _getComponentBoundaryNodes(self, compIDs): "constitutive object. This is not allowed. " "This constitutive object cannot use a " "panel-type constitutive object. " + f"On compID {compID}" f"CompIDs are: {repr(compIDs)}" ) nodeChain = nodeChain[:-1] diff --git a/tacs/constraints/panel_width.py b/tacs/constraints/panel_width.py new file mode 100644 index 000000000..c465b3607 --- /dev/null +++ b/tacs/constraints/panel_width.py @@ -0,0 +1,99 @@ +""" +Author: + - Sean P Engelstad, Alasdair Christison Gray + +This class implements a constraint which enforces the panel +width design variable values passed to elements using the GPBladeStiffenedShell +constitutive model to be consistent with the true width of the panel they are +a part of. + +.. note:: This class should be created using the + :meth:`pyTACS.createPanelWidthConstraint ` method. +""" + +# ============================================================================== +# Standard Python modules +# ============================================================================== + +# ============================================================================== +# External Python modules +# ============================================================================== +import numpy as np +import scipy as sp + +# ============================================================================== +# Extension modules +# ============================================================================== +from .panel_length import * + + +class PanelWidthConstraint(PanelLengthConstraint): + def __init__( + self, + name, + assembler, + comm, + outputViewer=None, + meshLoader=None, + options=None, + ): + """ + NOTE: This class should not be initialized directly by the user. + Use pyTACS.createPanelWidthConstraint instead. + + Parameters + ---------- + name : str + Name of this tacs problem + + assembler : TACS.Assembler + Cython object responsible for creating and setting tacs objects used to solve problem + + comm : mpi4py.MPI.Intracomm + The comm object on which to create the pyTACS object. + + outputViewer : TACS.TACSToFH5 + Cython object used to write out f5 files that can be converted and used for postprocessing. + + meshLoader : pymeshloader.pyMeshLoader + pyMeshLoader object used to create the assembler. + + options : dict + Dictionary holding problem-specific option parameters (case-insensitive). + """ + + super(PanelWidthConstraint, self).__init__( + name, assembler, comm, outputViewer, meshLoader, options + ) + + def computeRefAxis(self, refAxis: np.ndarray, comp_bndry_node_coords: np.ndarray): + """ + remove the panelNormal from the refAxis + + Parameters + ---------- + refAxis : numpy.ndarray + an array of size (3,) for the xyz coordinates of the original refAxis from transform object + comp_bndry_node_coords : numpy.ndarray + an array of size (3*N,) for the boundary nodal coordinates on the current panel / current TACS component + + Returns + ------- + numpy.ndarray + an array of size (3,) for xyz coords of the panel width axis in the panel + """ + + # For a more accurate length calculation, roject the ref axis + # onto the "average" plane of the baseline panel geometry by + # using an SVD to compute a normal vector + centroid = np.mean(comp_bndry_node_coords, axis=0, keepdims=True) + centredPoints = comp_bndry_node_coords - centroid + _, _, VT = np.linalg.svd(centredPoints, full_matrices=False) + panelNormal = VT[-1] + refAxis -= np.dot(refAxis, panelNormal) * panelNormal + refAxis /= np.linalg.norm(refAxis) + + # now this refAxis is the panel length axis, compute the width axis which is normal to the length + panel normal axis + widthAxis = np.cross(refAxis, panelNormal) + widthAxis /= np.linalg.norm(widthAxis) + return widthAxis diff --git a/tacs/cpp_headers/TACS.pxd b/tacs/cpp_headers/TACS.pxd index fa90410c3..cd046d4e9 100644 --- a/tacs/cpp_headers/TACS.pxd +++ b/tacs/cpp_headers/TACS.pxd @@ -338,7 +338,7 @@ cdef extern from "TACSAssembler.h": int getNumElements() TACSNodeMap *getNodeMap() TACSBcMap *getBcMap() - void getAverageStresses(ElementType elem_type, TacsScalar *avgStresses) + void getAverageStresses(ElementType elem_type, TacsScalar *avgStresses, int compNum) void setComplexStepGmatrix(bool flag) TACSElement **getElements() TACSElement *getElement(int, TacsScalar*, TacsScalar*, diff --git a/tacs/cpp_headers/constitutive.pxd b/tacs/cpp_headers/constitutive.pxd index 205eda4cd..f28d18db6 100644 --- a/tacs/cpp_headers/constitutive.pxd +++ b/tacs/cpp_headers/constitutive.pxd @@ -170,6 +170,84 @@ cdef extern from "TACSBladeStiffenedShellConstitutive.h": void setStiffenerPlyFractionBounds(TacsScalar[] lowerBound, TacsScalar[] upperBound) void setPanelPlyFractionBounds(TacsScalar[] lowerBound, TacsScalar[] upperBound) +cdef extern from "TACSGaussianProcessModel.h": + cdef cppclass TACSGaussianProcessModel: + TACSGaussianProcessModel( + int, # n_train + int, # n_param + TacsScalar[], # Xtrain + TacsScalar[], # alpha + TacsScalar[], # theta + ) + void setKS(TacsScalar ksWeight) + TacsScalar testAllGPTests(TacsScalar epsilon, int printLevel) + TacsScalar predictMeanTestData(TacsScalar*) + void setAlpha(TacsScalar*) + void setTheta(TacsScalar*) + TacsScalar kernel(TacsScalar*, TacsScalar*) + int getNparam() + int getNtrain() + void getTheta(TacsScalar*) + void getTrainingData(TacsScalar*) + +cdef extern from "TACSGaussianProcessModel.h": + cdef cppclass TACSBucklingGaussianProcessModel(TACSGaussianProcessModel): + TACSBucklingGaussianProcessModel( + int, # n_train + TacsScalar[], # Xtrain + TacsScalar[], # alpha + TacsScalar[], # theta + ) + +cdef extern from "TACSPanelGPs.h": + cdef cppclass TACSPanelGPs: + TACSPanelGPs( + TACSBucklingGaussianProcessModel*, # axial GP + TACSBucklingGaussianProcessModel*, # shear GP + TACSBucklingGaussianProcessModel*, # crippling GP + bool, + ) + +cdef extern from "TACSGPBladeStiffenedShellConstitutive.h": + cdef cppclass TACSGPBladeStiffenedShellConstitutive(TACSBladeStiffenedShellConstitutive): + TACSGPBladeStiffenedShellConstitutive( + TACSOrthotropicPly*, # panelPly + TACSOrthotropicPly*, # stiffenerPly + TacsScalar, # kcorr + TacsScalar, # panelLength + int, # panelLengthNum + TacsScalar, # stiffenerPitch + int, # stiffenerPitchNum + TacsScalar, # panelThick + int, # panelThickNum + int, # numPanelPlies + TacsScalar[], # panelPlyAngles + TacsScalar[], # panelPlyFracs + int[], # panelPlyFracNums + TacsScalar, # stiffenerHeight + int, # stiffenerHeightNum + TacsScalar, # stiffenerThick + int, # stiffenerThickNum + int, # numStiffenerPlies + TacsScalar[], # stiffenerPlyAngles + TacsScalar[], # stiffenerPlyFracs + int[], # stiffenerPlyFracNums + TacsScalar, # panelWidth + int, # panelWidthNum + TacsScalar, # flangeFraction, + bool, # CPTstiffenerCrippling + TACSPanelGPs*, # panelGPs object container + ) + TacsScalar nondimCriticalGlobalAxialLoad(TacsScalar rho_0, TacsScalar xi, TacsScalar gamma, TacsScalar zeta) + TacsScalar nondimCriticalLocalAxialLoad(TacsScalar rho_0, TacsScalar xi, TacsScalar zeta) + TacsScalar nondimCriticalGlobalShearLoad(TacsScalar rho_0, TacsScalar xi, TacsScalar gamma, TacsScalar zeta) + TacsScalar nondimCriticalLocalShearLoad(TacsScalar rho_0, TacsScalar xi, TacsScalar zeta) + TacsScalar nondimStiffenerCripplingLoad(TacsScalar rho_0, TacsScalar xi, TacsScalar genPoiss, TacsScalar zeta) + TacsScalar testAllTests(TacsScalar epsilon, int printLevel) + void setWriteDVMode(int mode) + void setCPTstiffenerCrippling(bool _mode) + + cdef extern from "TACSBeamConstitutive.h": cdef cppclass TACSBeamConstitutive(TACSConstitutive): pass diff --git a/tacs/pytacs.py b/tacs/pytacs.py index 1b5c6ea32..06a20af72 100755 --- a/tacs/pytacs.py +++ b/tacs/pytacs.py @@ -2166,6 +2166,36 @@ def createPanelLengthConstraint(self, name, options=None): constr.setNodes(self.Xpts0) return constr + @postinitialize_method + def createPanelWidthConstraint(self, name, options=None): + """Create a new PanelWidthConstraint for enforcing that the panel + width DV values passed to components match the actual panel widths. + + Parameters + ---------- + name : str + Name to assign constraint. + options : dict + Class-specific options to pass to DVConstraint instance (case-insensitive). + + Returns + ---------- + constraint : tacs.constraints.{PanelWidthConstraint} + PanelWidthConstraint object used for calculating constraints. + """ + constr = tacs.constraints.PanelWidthConstraint( + name, + self.assembler, + self.comm, + self.outputViewer, + self.meshLoader, + options, + ) + # Set with original design vars and coordinates, in case they have changed + constr.setDesignVars(self.x0) + constr.setNodes(self.Xpts0) + return constr + @postinitialize_method def createVolumeConstraint(self, name, options=None): """ diff --git a/tests/constitutive_tests/test_blade_sitffened_shell_constitutive.py b/tests/constitutive_tests/test_blade_stiffened_shell_constitutive.py similarity index 100% rename from tests/constitutive_tests/test_blade_sitffened_shell_constitutive.py rename to tests/constitutive_tests/test_blade_stiffened_shell_constitutive.py diff --git a/tests/constitutive_tests/test_gp_blade_shell_constitutive.py b/tests/constitutive_tests/test_gp_blade_shell_constitutive.py new file mode 100644 index 000000000..971087cc6 --- /dev/null +++ b/tests/constitutive_tests/test_gp_blade_shell_constitutive.py @@ -0,0 +1,458 @@ +import unittest + +import numpy as np + +from tacs import TACS, constitutive, elements + +DEG2RAD = np.pi / 180.0 +np.random.seed(1342342) + + +class GPConstitutiveMLTest(unittest.TestCase): + _my_debug = False + + def setUp(self): + + # fd/cs step size + if TACS.dtype is complex: + self.dh = 1e-200 + self.rtol = 1e-9 + else: + self.dh = 1e-8 + self.rtol = 1e-3 + self.dtype = TACS.dtype + + # The failure value returned by the model is an aggregate of multiple + # possible failure modes, we will run the failure tests multiple + # times to try and cover strain states that cause each failure mode + self.numFailureTests = 10 + + # Basically, only check relative tolerance + self.atol = self.rtol + self.print_level = 2 if self._my_debug else 0 + + # Set element index + self.elem_index = 0 + + # Set the variable arrays + self.x = np.ones(3, dtype=self.dtype) + self.pt = np.zeros(3) + + self.panelLength = 2.0 + self.panelLengthNum = 0 + self.stiffenerPitch = 0.2 + self.stiffenerPitchNum = 1 + self.stiffenerHeight = 0.075 + self.stiffenerHeightNum = 2 + self.stiffenerThickness = 1e-2 + self.stiffenerThicknessNum = 3 + self.panelThickness = 1.5e-2 + self.panelThicknessNum = 4 + + self.numPanelPlies = 3 + self.panelPlyAngles = np.array([0.0, 45.0, 90.0], dtype=self.dtype) * DEG2RAD + self.panelPlyFracs = np.random.random(self.numPanelPlies).astype(self.dtype) + self.panelPlyFracs /= np.sum(self.panelPlyFracs) # Make sure ply Fracs sum to 1 + self.panelPlyFracNums = np.arange(5, 5 + self.numPanelPlies, dtype=np.intc) + + self.numStiffenerPlies = 2 + self.stiffenerPlyAngles = np.array([0.0, 60.0], dtype=self.dtype) * DEG2RAD + self.stiffenerPlyFracs = np.random.random(self.numStiffenerPlies).astype( + self.dtype + ) + self.stiffenerPlyFracs /= np.sum( + self.stiffenerPlyFracs + ) # Make sure ply Fracs sum to 1 + self.stiffenerPlyFracNums = np.arange( + 5 + self.numPanelPlies, + 5 + self.numPanelPlies + self.numStiffenerPlies, + dtype=np.intc, + ) + + self.panelWidth = 1.0 + self.panelWidthNum = 5 + self.numPanelPlies + self.numStiffenerPlies + + self.dvs = ( + [ + self.panelLength, + self.stiffenerPitch, + self.stiffenerHeight, + self.stiffenerThickness, + self.panelThickness, + ] + + list(self.panelPlyFracs) + + list(self.stiffenerPlyFracs) + + [self.panelWidth] + ) + self.dvs = np.array(self.dvs, dtype=self.dtype) + + self.flangeFraction = 0.8 + self.kcorr = 5.0 / 6.0 + + # Create the isotropic layup + rho = 2700.0 + specific_heat = 921.096 + E = 70e3 + nu = 0.3 + ys = 270.0 + cte = 24.0e-6 + kappa = 230.0 + iso_prop = constitutive.MaterialProperties( + rho=rho, + specific_heat=specific_heat, + E=E, + nu=nu, + ys=ys, + alpha=cte, + kappa=kappa, + ) + iso_ply = constitutive.OrthotropicPly(1e-3, iso_prop) + + # Create the orthotropic layup + rho = 1550.0 + specific_heat = 921.096 + E1 = 54e3 + E2 = 18e3 + nu12 = 0.25 + G12 = 9e3 + G13 = 9e3 + Xt = 2410.0 + Xc = 1040.0 + Yt = 73.0 + Yc = 173.0 + S12 = 71.0 + cte = 24.0e-6 + kappa = 230.0 + ortho_prop = constitutive.MaterialProperties( + rho=rho, + specific_heat=specific_heat, + E1=E1, + E2=E2, + nu12=nu12, + G12=G12, + G13=G13, + G23=G13, + Xt=Xt, + Xc=Xc, + Yt=Yt, + Yc=Yc, + S12=S12, + alpha=cte, + kappa=kappa, + ) + ortho_ply = constitutive.OrthotropicPly(1e-3, ortho_prop) + + self.ply_list = [iso_ply, ortho_ply] + + self.failure_modes = [ + "PanelMaterialFailure", + "StiffenerMaterialFailure", + "LocalBuckling", + "GlobalBuckling", + "StiffenerColumnBuckling", + "StiffenerCrippling", + ] + self.failure_modes_to_test = self.failure_modes + ["all"] + + # Seed random number generator in tacs for consistent test results + elements.SeedRandomGenerator(0) + + # construct the optional ML models + n_train = 100 + + n_param = constitutive.BucklingGP.n_param + self.axialGP = constitutive.BucklingGP( + n_train, + Xtrain=np.random.rand(n_param * n_train).astype(self.dtype), + alpha=np.random.rand(n_train).astype(self.dtype), + theta=np.random.rand(14).astype(self.dtype), + ) + # self.axialGP.setKS(0.1) + + n_param = constitutive.BucklingGP.n_param + self.shearGP = constitutive.BucklingGP( + n_train, + Xtrain=np.random.rand(n_param * n_train).astype(self.dtype), + alpha=np.random.rand(n_train).astype(self.dtype), + theta=np.random.rand(14).astype(self.dtype), + ) + # self.shearGP.setKS(0.1) + + self.cripplingGP = None + + self.panelGPs = constitutive.PanelGPs( + self.axialGP, self.shearGP, self.cripplingGP, saveData=False + ) + + def get_con(self, ply): + con = constitutive.GPBladeStiffenedShellConstitutive( + ply, + ply, + self.panelLength, + self.stiffenerPitch, + self.panelThickness, + self.panelPlyAngles, + self.panelPlyFracs, + self.stiffenerHeight, + self.stiffenerThickness, + self.stiffenerPlyAngles, + self.stiffenerPlyFracs, + self.panelWidth, + self.kcorr, + self.flangeFraction, + self.panelLengthNum, + self.stiffenerPitchNum, + self.panelThicknessNum, + self.panelPlyFracNums, + self.stiffenerHeightNum, + self.stiffenerThicknessNum, + self.stiffenerPlyFracNums, + self.panelWidthNum, + panelGPs=self.panelGPs, + ) + # Set the KS weight really low so that all failure modes make a + # significant contribution to the failure function derivatives + con.setKSWeight(0.1) + return con + + def test_constitutive_density(self): + # Test density dv sensitivity + for ply in self.ply_list: + with self.subTest(ply=ply): + con = self.get_con(ply) + fail = constitutive.TestConstitutiveDensity( + con, + self.elem_index, + self.pt, + self.x, + self.dvs, + self.dh, + self.print_level, + self.atol, + self.rtol, + ) + self.assertFalse(fail) + + # def test_constitutive_specific_heat(self): + # # Test specific heat dv sensitivity + # for ply in self.ply_list: + # with self.subTest(ply=ply): + # con = self.get_con(ply) + # fail = constitutive.TestConstitutiveSpecificHeat( + # con, + # self.elem_index, + # self.pt, + # self.x, + # self.dvs, + # self.dh, + # self.print_level, + # self.atol, + # self.rtol, + # ) + # self.assertFalse(fail) + + # def test_constitutive_heat_flux(self): + # # Test heat flux dv sensitivity + # for ply in self.ply_list: + # with self.subTest(ply=ply): + # con = self.get_con(ply) + # fail = constitutive.TestConstitutiveHeatFlux( + # con, + # self.elem_index, + # self.pt, + # self.x, + # self.dvs, + # self.dh, + # self.print_level, + # self.atol, + # self.rtol, + # ) + # self.assertFalse(fail) + + def test_constitutive_stress(self): + # Test stress dv sensitivity + for ply in self.ply_list: + with self.subTest(ply=ply): + con = self.get_con(ply) + fail = constitutive.TestConstitutiveStress( + con, + self.elem_index, + self.pt, + self.x, + self.dvs, + self.dh, + self.print_level, + self.atol, + self.rtol, + ) + self.assertFalse(fail) + + # def test_constitutive_thermal_strain(self): + # # Test thermal strain dv sensitivity + # for ply in self.ply_list: + # with self.subTest(ply=ply): + # con = self.get_con(ply) + # fail = constitutive.TestConstitutiveThermalStrain( + # con, + # self.elem_index, + # self.pt, + # self.x, + # self.dvs, + # self.dh, + # self.print_level, + # self.atol, + # self.rtol, + # ) + # self.assertFalse(fail) + + def test_constitutive_failure_dv_sens(self): + # Test failure dv sensitivity + for ply in self.ply_list: + with self.subTest(ply=ply): + for enabled_failure_mode in self.failure_modes_to_test: + with self.subTest(failure_mode=enabled_failure_mode): + includeFailureModes = {} + for failure_mode in self.failure_modes: + includeFailureModes[f"include{failure_mode}"] = ( + failure_mode == enabled_failure_mode + or enabled_failure_mode == "all" + ) + con = self.get_con(ply) + con.setFailureModes(**includeFailureModes) + fail = False + for _ in range(self.numFailureTests): + fail = fail or constitutive.TestConstitutiveFailure( + con, + self.elem_index, + self.pt, + self.x, + self.dvs, + self.dh, + self.print_level, + self.atol, + self.rtol, + ) + self.assertFalse(fail) + + def test_constitutive_failure_strain_sens(self): + for ply in self.ply_list: + with self.subTest(ply=ply): + for enabled_failure_mode in self.failure_modes_to_test: + with self.subTest(failure_mode=enabled_failure_mode): + includeFailureModes = {} + for failure_mode in self.failure_modes: + includeFailureModes[f"include{failure_mode}"] = ( + failure_mode == enabled_failure_mode + or enabled_failure_mode == "all" + ) + con = self.get_con(ply) + con.setFailureModes(**includeFailureModes) + fail = False + for _ in range(self.numFailureTests): + fail = ( + fail + or constitutive.TestConstitutiveFailureStrainSens( + con, + self.elem_index, + self.pt, + self.x, + self.dh, + self.print_level, + self.rtol, + self.atol, + ) + ) + self.assertFalse(fail) + + def _constitutive_internal_tests(self): + """test all the internal or intermediate tests in C++""" + for ply in self.ply_list: + with self.subTest(ply=ply): + con = self.get_con(ply) + if TACS.dtype is complex: + dh = self.dh * 1j + else: + dh = self.dh + relError = con.test_all_derivative_tests( + dh, self.print_level + ) # self.print_level + fail = abs(relError.real) > self.rtol + self.assertFalse(fail) + if self.print_level != 0: + print("All internal tests pass!") + + +class GPConstitutiveCFTest(GPConstitutiveMLTest): + """ + second test with closed-form only, not ML models + """ + + def setUp(self): + super(GPConstitutiveCFTest, self).setUp() + + # change the ML models to None so the constitutive class + # uses closed-form solutions for buckling now. + self.axialGP = None + self.shearGP = None + self.cripplingGP = None + self.panelGPs = constitutive.PanelGPs( + self.axialGP, + self.shearGP, + self.cripplingGP, + ) + + +if __name__ == "__main__": + # current status of these tests + # case == "CF" [runs internal tests] => all pass + # case == "ML" [runs internal tests] => all pass + # case == "all" [runs internal + external tests] => failure criterion fails. + + import argparse + + parent_parser = argparse.ArgumentParser(add_help=False) + parent_parser.add_argument("--case", type=str, default="full") + args = parent_parser.parse_args() + + cases = ["CF", "ML", "full"] + cases += ["failStrain-CF", "failStrain-ML"] + cases += ["failDV-CF", "failDV-ML"] + + assert args.case in cases + + if args.case == "CF": + tester = GPConstitutiveCFTest() + tester._my_debug = True + tester.setUp() + tester._constitutive_internal_tests() + elif args.case == "ML": + tester = GPConstitutiveMLTest() + tester._my_debug = True + tester.setUp() + tester._constitutive_internal_tests() + elif args.case == "failStrain-CF": + # shouldn't matter which one of these I test as long as internal tests pass + tester = GPConstitutiveCFTest() + tester._my_debug = True + tester.setUp() + tester.test_constitutive_failure_strain_sens() + elif args.case == "failStrain-ML": + # shouldn't matter which one of these I test as long as internal tests pass + tester = GPConstitutiveMLTest() + tester._my_debug = True + tester.setUp() + tester.test_constitutive_failure_strain_sens() + elif args.case == "failDV-CF": + # shouldn't matter which one of these I test as long as internal tests pass + tester = GPConstitutiveCFTest() + tester._my_debug = True + tester.setUp() + tester.test_constitutive_failure_dv_sens() + elif args.case == "failDV-ML": + # shouldn't matter which one of these I test as long as internal tests pass + tester = GPConstitutiveMLTest() + tester._my_debug = True + tester.setUp() + tester.test_constitutive_failure_dv_sens() + elif args.case == "full": + unittest.main()