Source code for openaerostruct.structures.transform
import numpy as np
import openmdao.api as om
from openaerostruct.utils.vector_algebra import add_ones_axis
from openaerostruct.utils.vector_algebra import compute_norm, compute_norm_deriv
from openaerostruct.utils.vector_algebra import compute_cross, compute_cross_deriv1, compute_cross_deriv2
[docs]
class Transform(om.ExplicitComponent):
def initialize(self):
self.options.declare("surface", types=dict)
def setup(self):
surface = self.options["surface"]
self.ny = ny = surface["mesh"].shape[1]
self.add_input("nodes", shape=(ny, 3), units="m")
self.add_output("transform", shape=(ny - 1, 12, 12))
mesh_indices = np.arange(3 * ny).reshape((ny, 3))
transform_indices = np.arange(144 * (ny - 1)).reshape((ny - 1, 12, 12))
rows = np.concatenate(
[
np.einsum("ijk,l->ijkl", transform_indices, np.ones(3, int)).flatten(),
np.einsum("ijk,l->ijkl", transform_indices, np.ones(3, int)).flatten(),
]
)
cols = np.concatenate(
[
np.einsum("il,jk->ijkl", mesh_indices[:-1, :], np.ones((12, 12), int)).flatten(),
np.einsum("il,jk->ijkl", mesh_indices[1:, :], np.ones((12, 12), int)).flatten(),
]
)
self.declare_partials("transform", "nodes", rows=rows, cols=cols)
self.ref_axis = np.outer(np.ones(ny - 1), np.array([1.0, 0.0, 0.0]))
def compute(self, inputs, outputs):
P0 = inputs["nodes"][:-1, :]
P1 = inputs["nodes"][1:, :]
norm = compute_norm(P1 - P0)
row0 = (P1 - P0) / norm
cross = compute_cross(row0, self.ref_axis)
norm = compute_norm(cross)
row1 = cross / norm
cross = compute_cross(row0, row1)
row2 = cross
outputs["transform"] = 0.0
for k in range(4):
outputs["transform"][:, 3 * k + 0, 3 * k : 3 * k + 3] = row0
outputs["transform"][:, 3 * k + 1, 3 * k : 3 * k + 3] = row1
outputs["transform"][:, 3 * k + 2, 3 * k : 3 * k + 3] = row2
def compute_partials(self, inputs, partials):
surface = self.options["surface"]
ny = surface["mesh"].shape[1]
P0 = inputs["nodes"][:-1, :]
P1 = inputs["nodes"][1:, :]
P_deriv = np.einsum("i,jk->ijk", np.ones(ny - 1), np.eye(3))
norm = compute_norm(P1 - P0)
norm_deriv = compute_norm_deriv(P1 - P0, P_deriv)
row0 = (P1 - P0) / norm
row0_deriv = P_deriv / add_ones_axis(norm) - add_ones_axis(P1 - P0) / add_ones_axis(norm) ** 2 * norm_deriv
cross = compute_cross(row0, self.ref_axis)
cross_deriv = compute_cross_deriv1(row0_deriv, self.ref_axis)
norm = compute_norm(cross)
norm_deriv = compute_norm_deriv(cross, cross_deriv)
row1 = cross / norm
row1_deriv = cross_deriv / add_ones_axis(norm) - add_ones_axis(cross) / add_ones_axis(norm) ** 2 * norm_deriv
cross = compute_cross(row0, row1)
cross_deriv = compute_cross_deriv1(row0_deriv, row1) + compute_cross_deriv2(row0, row1_deriv)
row2_deriv = cross_deriv
derivs = partials["transform", "nodes"].reshape((2, ny - 1, 12, 12, 3))
for k in range(4):
derivs[0, :, 3 * k + 0, 3 * k : 3 * k + 3, :] = -row0_deriv
derivs[1, :, 3 * k + 0, 3 * k : 3 * k + 3, :] = row0_deriv
derivs[0, :, 3 * k + 1, 3 * k : 3 * k + 3, :] = -row1_deriv
derivs[1, :, 3 * k + 1, 3 * k : 3 * k + 3, :] = row1_deriv
derivs[0, :, 3 * k + 2, 3 * k : 3 * k + 3, :] = -row2_deriv
derivs[1, :, 3 * k + 2, 3 * k : 3 * k + 3, :] = row2_deriv