class WBNumTessellationInvest(WBNumTessellationBase):
    """ A class to investigate the angles for tessellating three cells manually. """

    name = 'WBNumTessellationInvest'

    rot_br = bu.Float(0.5)
    rot_ur = bu.Float(0.5)
    investigate_rot = bu.Bool

    ipw_view = bu.View(
        *WBNumTessellationBase.ipw_view.content,
        bu.Item('investigate_rot'),
        bu.Item('rot_br',
                latex=r'rot~br',
                editor=bu.FloatRangeEditor(low=0,
                                           high=2 * np.pi,
                                           n_steps=150,
                                           continuous_update=True)),
        bu.Item('rot_ur',
                latex=r'rot~ur',
                editor=bu.FloatRangeEditor(low=0,
                                           high=2 * np.pi,
                                           n_steps=150,
                                           continuous_update=True)),
    )

    def setup_plot(self, pb):
        super().setup_plot(pb)

    def update_plot(self, pb):
        if self.k3d_mesh:
            sol = self.sol
            X_Ia = self.X_Ia.astype(np.float32)
            br_X_Ia = self._get_br_X_Ia(
                self.X_Ia,
                self.rot_br if self.investigate_rot else sol[0]).astype(
                    np.float32)
            ur_X_Ia = self._get_ur_X_Ia(
                self.X_Ia,
                self.rot_ur if self.investigate_rot else sol[1]).astype(
                    np.float32)
            self.k3d_mesh['X_Ia'].vertices = X_Ia
            self.k3d_mesh['br_X_Ia'].vertices = br_X_Ia
            self.k3d_mesh['ur_X_Ia'].vertices = ur_X_Ia
            self.k3d_wireframe['X_Ia'].vertices = X_Ia
            self.k3d_wireframe['br_X_Ia'].vertices = br_X_Ia
            self.k3d_wireframe['ur_X_Ia'].vertices = ur_X_Ia
        else:
            self.setup_plot(pb)
Esempio n. 2
0
class LiDamageFn(DamageFn):

    name = 'Two parameter damage'

    latex_eq = Str(r'''Damage function (Li)
        \begin{align}
        \omega = g(\kappa) = \frac{\alpha_1}{1 + \exp(-\alpha_2 \kappa + 6 )}
        \end{align}
        where $\kappa$ is the state variable representing 
        the maximum slip that occurred so far in
        in the history of loading.
        ''')

    alpha_1 = bu.Float(
        value=1,
        MAT=True,
        symbol=r'\alpha_1',
        unit='-',
        desc="parameter controlling the shape of the damage function")

    alpha_2 = bu.Float(
        2000.,
        MAT=True,
        symbol=r'\alpha_2',
        unit='-',
        desc="parameter controlling the shape of the damage function")

    def __call__(self, kappa):
        alpha_1 = self.alpha_1
        alpha_2 = self.alpha_2
        s_0 = self.s_0
        omega = np.zeros_like(kappa, dtype=np.float_)
        d_idx = np.where(kappa >= s_0)[0]
        k = kappa[d_idx]
        omega[d_idx] = 1. / \
            (1. + np.exp(-1. * alpha_2 * (k - s_0) + 6.)) * alpha_1
        return omega

    def diff(self, kappa):
        alpha_1 = self.alpha_1
        alpha_2 = self.alpha_2
        s_0 = self.s_0
        return ((alpha_1 * alpha_2 * np.exp(-1. * alpha_2 *
                                            (kappa - s_0) + 6.)) /
                (1 + np.exp(-1. * alpha_2 * (kappa - s_0) + 6.))**2)

    ipw_view = bu.View(
        bu.Item('s_0'),
        bu.Item('alpha_1', editor=bu.FloatRangeEditor(low=0, high=1)),
        bu.Item('alpha_2'),
    )
Esempio n. 3
0
class WBShellAnalysis(TStepBC, bu.InteractiveModel):
    name = 'WBShellAnalysis'
    plot_backend = 'k3d'
    id = bu.Str
    """ if you saved boundary conditions for your current analysis, this id will make sure these bcs
     are loaded automatically next time you create an instance with the same id """

    h = bu.Float(10, GEO=True)
    show_wireframe = bu.Bool(True, GEO=True)

    ipw_view = bu.View(
        bu.Item('h',
                editor=bu.FloatRangeEditor(low=1, high=100, n_steps=100),
                continuous_update=False),
        bu.Item('show_wireframe'),
        time_editor=bu.ProgressEditor(run_method='run',
                                      reset_method='reset',
                                      interrupt_var='interrupt',
                                      time_var='t',
                                      time_max='t_max'),
    )

    n_phi_plus = tr.Property()

    def _get_n_phi_plus(self):
        return self.xdomain.mesh.n_phi_plus

    tree = ['geo', 'bcs', 'tmodel', 'xdomain']

    geo = bu.Instance(WBShellGeometry4P, ())

    tmodel = bu.Instance(MATS2DElastic, ())
    # tmodel = bu.Instance(MATSShellElastic, ())

    bcs = bu.Instance(BoundaryConditions)
    def _bcs_default(self):
        return BoundaryConditions(geo=self.geo, n_nodal_dofs=self.xdomain.fets.n_nodal_dofs, id=self.id)

    xdomain = tr.Property(tr.Instance(TriXDomainFE),
                          depends_on="state_changed")
    '''Discretization object.'''

    @tr.cached_property
    def _get_xdomain(self):
        # prepare the mesh generator
        # mesh = WBShellFETriangularMesh(geo=self.geo, direct_mesh=False, subdivision=2)
        mesh = WBShellFETriangularMesh(geo=self.geo, direct_mesh=True)
        # construct the domain with the kinematic strain mapper and stress integrator
        return TriXDomainFE(
            mesh=mesh,
            integ_factor=self.h,
        )

        # mesh = WBShellFETriangularMesh(geo=self.geo, direct_mesh=True)
        # mesh.fets = FETS2DMITC(a= self.h)
        # return TriXDomainMITC(
        #     mesh=mesh
        # )

    domains = tr.Property(depends_on="state_changed")

    @tr.cached_property
    def _get_domains(self):
        return [(self.xdomain, self.tmodel)]

    def reset(self):
        self.sim.reset()

    t = tr.Property()

    def _get_t(self):
        return self.sim.t

    def _set_t(self, value):
        self.sim.t = value

    t_max = tr.Property()

    def _get_t_max(self):
        return self.sim.t_max

    def _set_t_max(self, value):
        self.sim.t_max = value

    interrupt = tr.Property()

    def _get_interrupt(self):
        return self.sim.interrupt

    def _set_interrupt(self, value):
        self.sim.interrupt = value

    bc = tr.Property(depends_on="state_changed")
    # @tr.cached_property
    def _get_bc(self):
        bc_fixed, _, _ = self.bcs.bc_fixed
        bc_loaded, _, _ = self.bcs.bc_loaded
        return bc_fixed + bc_loaded

    def run(self):
        s = self.sim
        s.tloop.k_max = 10
        s.tline.step = 1
        s.tloop.verbose = False
        s.run()

    def get_max_vals(self):
        self.run()
        U_1 = self.hist.U_t[-1]
        U_max = np.max(np.fabs(U_1))
        return U_max

    def export_abaqus(self):
        al = AbaqusLink(shell_analysis=self)
        al.model_name = 'test_name'
        al.build_inp()

    def setup_plot(self, pb):
        print('analysis: setup_plot')
        X_Id = self.xdomain.mesh.X_Id
        if len(self.hist.U_t) == 0:
            U_1 = np.zeros_like(X_Id)
            print('analysis: U_I', )
        else:
            U_1 = self.hist.U_t[-1]
            U_1 = U_1.reshape(-1, self.xdomain.fets.n_nodal_dofs)[:, :3]

        X1_Id = X_Id + U_1
        X1_Id = X1_Id.astype(np.float32)

        I_Ei = self.xdomain.I_Ei.astype(np.uint32)

        # Original state mesh
        wb_mesh_0 = k3d.mesh(self.xdomain.X_Id.astype(np.float32),
                             I_Ei,
                             color=0x999999, opacity=0.5,
                             side='double')
        pb.plot_fig += wb_mesh_0
        pb.objects['wb_mesh_0'] = wb_mesh_0

        # Deformed state mesh
        wb_mesh_1 = k3d.mesh(X1_Id,
                             I_Ei,
                             color_map=k3d.colormaps.basic_color_maps.Jet,
                             attribute=U_1[:, 2],
                             color_range=[np.min(U_1), np.max(U_1)],
                             side='double')
        pb.plot_fig += wb_mesh_1
        pb.objects['wb_mesh_1'] = wb_mesh_1

        if self.show_wireframe:
            k3d_mesh_wireframe = k3d.mesh(X1_Id,
                                          I_Ei,
                                          color=0x000000,
                                          wireframe=True)
            pb.plot_fig += k3d_mesh_wireframe
            pb.objects['mesh_wireframe'] = k3d_mesh_wireframe

    def update_plot(self, pb):
        X_Id = self.xdomain.mesh.X_Id
        print('analysis: update_plot')
        if len(self.hist.U_t) == 0:
            U_1 = np.zeros_like(X_Id)
            print('analysis: U_I', )
        else:
            U_1 = self.hist.U_t[-1]
            U_1 = U_1.reshape(-1, self.xdomain.fets.n_nodal_dofs)[:, :3]

        X1_Id = X_Id + U_1
        X1_Id = X1_Id.astype(np.float32)

        I_Ei = self.xdomain.I_Ei.astype(np.uint32)

        mesh = pb.objects['wb_mesh_1']
        mesh.vertices = X1_Id
        mesh.indices = I_Ei
        mesh.attribute = U_1[:, 2]
        mesh.color_range = [np.min(U_1), np.max(U_1)]
        if self.show_wireframe:
            wireframe = pb.objects['mesh_wireframe']
            wireframe.vertices = X1_Id
            wireframe.indices = I_Ei

    def get_Pw(self):
        import numpy as np
        F_to = self.hist.F_t
        U_to = self.hist.U_t
        _, _, loaded_dofs = self.bcs.bc_loaded
        F_loaded = np.sum(F_to[:, loaded_dofs], axis=-1)
        U_loaded = np.average(U_to[:, loaded_dofs], axis=-1)
        return U_loaded, F_loaded
Esempio n. 4
0
class WBCell5ParamV2(WBCell):
    name = 'waterbomb cell 5p v2'

    plot_backend = 'k3d'

    gamma = bu.Float(np.pi / 6, GEO=True)
    a = bu.Float(500, GEO=True)
    b = bu.Float(750, GEO=True)
    c = bu.Float(400, GEO=True)
    # beta = bu.Float(np.pi / 3, GEO=True)
    beta = tr.Property(depends_on='+GEO')

    @tr.cached_property
    def _get_beta(self):
        return round(self.beta_1 + self.beta_0, 8)

    beta_1 = bu.Float(0, GEO=True)
    beta_0 = tr.Property(depends_on='+GEO')

    @tr.cached_property
    def _get_beta_0(self):
        """ This is the value of beta that makes the cell symmetric, derived in wb_cell_4p_deriving_beta_0.ipynb"""
        return np.arccos(self.a * (1 - 2 * sin(self.gamma)) /
                         sqrt(self.a**2 + self.b**2))

    continuous_update = True

    ipw_view = bu.View(
        bu.Item('gamma',
                latex=r'\gamma',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high=np.pi / 2,
                    n_steps=501,
                    continuous_update=continuous_update)),
        # bu.Item('beta', latex=r'\beta', editor=bu.FloatRangeEditor(
        #     low=1e-6, high=np.pi - 1e-6, n_steps=501, continuous_update=continuous_update)),
        bu.Item('beta_1',
                latex=r'\beta_1',
                editor=bu.FloatRangeEditor(
                    low=-4,
                    high=4,
                    n_steps=501,
                    continuous_update=continuous_update)),
        bu.Item('a',
                latex='a',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high=2000,
                    n_steps=201,
                    continuous_update=continuous_update)),
        bu.Item('b',
                latex='b',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high=2000,
                    n_steps=201,
                    continuous_update=continuous_update)),
        bu.Item('c',
                latex='c',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high=2000,
                    n_steps=201,
                    continuous_update=continuous_update)),
        *WBCell.ipw_view.content,
    )

    X_Ia = tr.Property(depends_on='+GEO')
    '''Array with nodal coordinates I - node, a - dimension
    '''

    @tr.cached_property
    def _get_X_Ia(self):
        return self.get_cell_vertices()

    def get_cell_vertices(self,
                          a=0.5,
                          b=0.75,
                          c=0.4,
                          gamma=np.pi / 6,
                          beta=np.pi / 3):
        a = self.a
        b = self.b
        c = self.c
        gamma = self.gamma
        beta = self.beta

        # phi1 is angle between OU_ur line and z axis
        cos_psi1 = ((b**2 - a**2) - a * sqrt(a**2 + b**2) * cos(beta)) / (
            b * sqrt(a**2 + b**2) * sin(beta))
        sin_psi1 = sqrt(a**2 * (3 * b**2 - a**2) + 2 * a *
                        (b**2 - a**2) * sqrt(a**2 + b**2) * cos(beta) -
                        (a**2 + b**2)**2 * cos(beta)**2) / (
                            b * sqrt(a**2 + b**2) * sin(beta))
        cos_psi5 = (sqrt(a**2 + b**2) * cos(beta) -
                    a * cos(2 * gamma)) / (b * sin(2 * gamma))
        sin_psi5 = sqrt(
            b**2 + 2 * a * sqrt(a**2 + b**2) * cos(beta) * cos(2 * gamma) -
            (a**2 + b**2) *
            (cos(beta)**2 + cos(2 * gamma)**2)) / (b * sin(2 * gamma))
        cos_psi6 = (a - sqrt(a**2 + b**2) * cos(beta) * cos(2 * gamma)) / (
            sqrt(a**2 + b**2) * sin(beta) * sin(2 * gamma))
        sin_psi6 = sqrt(b**2 + 2 * a * sqrt(a**2 + b**2) * cos(beta) *
                        cos(2 * gamma) - (a**2 + b**2) *
                        (cos(beta)**2 + cos(2 * gamma)**2)) / (
                            sqrt(a**2 + b**2) * sin(beta) * sin(2 * gamma))
        cos_psi1plus6 = cos_psi1 * cos_psi6 - sin_psi1 * sin_psi6
        sin_psi1plus6 = sin_psi1 * cos_psi6 + cos_psi1 * sin_psi6

        cos_phi1 = cos_psi1plus6
        cos_phi2 = cos_psi5
        cos_phi3 = cos_psi5
        cos_phi4 = cos_psi1plus6
        sin_phi1 = sin_psi1plus6
        sin_phi2 = sin_psi5
        sin_phi3 = sin_psi5
        sin_phi4 = sin_psi1plus6

        U_ur = np.array([
            a * sin(gamma) - b * cos_phi1 * cos(gamma), b * sin_phi1,
            a * cos(gamma) + b * cos_phi1 * sin(gamma)
        ])
        U_ul = np.array([
            -a * sin(gamma) + b * cos_phi2 * cos(gamma), b * sin_phi2,
            a * cos(gamma) + b * cos_phi2 * sin(gamma)
        ])
        U_lr = np.array([
            a * sin(gamma) - b * cos_phi3 * cos(gamma), -b * sin_phi3,
            a * cos(gamma) + b * cos_phi3 * sin(gamma)
        ])
        U_ll = np.array([
            -a * sin(gamma) + b * cos_phi4 * cos(gamma), -b * sin_phi4,
            a * cos(gamma) + b * cos_phi4 * sin(gamma)
        ])
        V_r = np.array([c * sin(gamma), 0, c * cos(gamma)])
        V_l = np.array([-c * sin(gamma), 0, c * cos(gamma)])

        X_Ia = np.vstack(
            (np.zeros(3), U_lr, U_ll, U_ur, U_ul, V_r, V_l)).astype(np.float32)
        return X_Ia
Esempio n. 5
0
class FDoubleCap(bu.Model, bu.InjectSymbExpr):

    name = 'Threshold'

    symb_class = FDoubleCapExpr

    f_t = bu.Float(5, MAT=True)
    f_c = bu.Float(80, MAT=True)
    f_c0 = bu.Float(30, MAT=True)
    tau_bar = bu.Float(5, MAT=True)
    m = bu.Float(0.1, MAT=True)
    z_scale = bu.Float(1)

    ipw_view = bu.View(
        bu.Item('f_t', editor=bu.FloatRangeEditor(low=1, high=max_f_t)),
        bu.Item('f_c', editor=bu.FloatRangeEditor(low=10, high=max_f_c)),
        bu.Item('f_c0',
                latex='f_{c0}',
                editor=bu.FloatRangeEditor(low=5, high=0.9 * max_f_c)),
        bu.Item('tau_bar',
                latex=r'\bar{\tau}',
                editor=bu.FloatRangeEditor(low=1, high=max_tau_bar)),
        bu.Item('m', minmax=(0.0001, 0.5)),
        bu.Item('z_scale',
                latex=r'\eta_{z}',
                editor=bu.FloatRangeEditor(low=0, high=1)),
    )

    plot_backend = 'mpl'

    def update_plot(self, pb):
        delta_f = 0.1 * self.f_t
        max_tau_bar = self.tau_bar * 1.1
        min_sig = -self.f_c - delta_f
        max_sig = self.f_t + delta_f
        X_a, Y_a = np.mgrid[-min_sig:max_sig:210j,
                            -max_tau_bar:max_tau_bar:210j]
        Z_a = self.symb.get_f_solved(X_a, Y_a) * self.z_scale
        Z_0 = np.zeros_like(Z_a)
        self.surface.heights = Z_a.astype(np.float32)
        self.surface0.heights = Z_0.astype(np.float32)

    def subplots(self, fig):
        #ax = fig.subplots(1, 1)
        ax = fig.add_subplot(1, 1, 1, projection='3d')
        return ax

    def get_XYZ_grid(self):  #
        delta_f = 2 * self.f_t
        min_sig = -self.f_c - delta_f
        max_sig = self.f_t + delta_f
        max_tau_bar = self.tau_bar + self.m * self.f_c0 + delta_f
        X_a, Y_a = np.mgrid[min_sig:max_sig:210j,
                            -max_tau_bar:max_tau_bar:210j]
        Z_a = self.symb.get_f_solved(X_a, Y_a)
        return X_a, Y_a, Z_a

    def plot_3d(self, ax):
        X_a, Y_a, Z_a = self.get_XYZ_grid()
        Z_0 = np.zeros_like(Z_a)
        ax.plot_surface(X_a,
                        Y_a,
                        Z_a,
                        rstride=1,
                        cstride=1,
                        cmap='winter',
                        edgecolor='none')
        ax.plot_surface(X_a, Y_a, Z_0, edgecolor='none')
        ax.set_title('threshold function')

    def plot_contour(self, ax):
        X_a, Y_a, Z_a = self.get_XYZ_grid()
        ax.contour(X_a, Y_a, Z_a)  #, levels=[0])
        ax.set_title('threshold function')

    def update_plot(self, ax):
        # Evaluate the threshold function within an orthogonal grid
        self.plot_contour(ax)
Esempio n. 6
0
class WBCell4Param(WBCell, bu.InjectSymbExpr):
    name = 'Waterbomb cell 4p'
    symb_class = WBCellSymb4Param

    plot_backend = 'k3d'

    gamma = bu.Float(np.pi/2-0.001, GEO=True)
    a = bu.Float(1000, GEO=True)
    b = bu.Float(1000, GEO=True)
    c = bu.Float(1000, GEO=True)
    a_high = bu.Float(2000)
    b_high = bu.Float(2000)
    c_high = bu.Float(2000)

    ipw_view = bu.View(
        bu.Item('gamma', latex=r'\gamma', editor=bu.FloatRangeEditor(
            low=1e-6, high=np.pi / 2, n_steps=101, continuous_update=True)),
        bu.Item('a', latex='a', editor=bu.FloatRangeEditor(
            low=1e-6, high_name='a_high', n_steps=101, continuous_update=True)),
        bu.Item('b', latex='b', editor=bu.FloatRangeEditor(
            low=1e-6, high_name='b_high', n_steps=101, continuous_update=True)),
        bu.Item('c', latex='c', editor=bu.FloatRangeEditor(
            low=1e-6, high_name='c_high', n_steps=101, continuous_update=True)),
        *WBCell.ipw_view.content,
    )

    n_I = tr.Property
    def _get_n_I(self):
        return len(self.X_Ia)

    X_Ia = tr.Property(depends_on='+GEO')
    '''Array with nodal coordinates I - node, a - dimension
    '''

    @tr.cached_property
    def _get_X_Ia(self):
        gamma = self.gamma
        u_2 = self.symb.get_u_2_()
        u_3 = self.symb.get_u_3_()
        return np.array([
            [0, 0, 0],  # 0 point
            [self.a, u_2, u_3],  # U++
            [-self.a, u_2, u_3],  # U-+
            [self.a, -u_2, u_3],  # U+-
            [-self.a, -u_2, u_3],  # U--
            [self.c * np.sin(gamma), 0, self.c * np.cos(gamma)],  # W0+
            [-self.c * np.sin(gamma), 0, self.c * np.cos(gamma)]  # W0-
        ], dtype=np.float_
        )

    I_boundary = tr.Array(np.int_, value=[[2, 1],
                                          [6, 5],
                                          [4, 3], ])
    '''Boundary nodes in 2D array to allow for generation of shell boundary nodes'''

    # X_theta_Ia = tr.Property(depends_on='+GEO')
    # '''Array with nodal coordinates I - node, a - dimension
    # '''
    # @tr.cached_property
    # def _get_X_theta_Ia(self):
    #     D_a = self.symb.get_D_(self.gamma).T
    #     theta = self.symb.get_theta_sol(self.gamma)
    #     XD_Ia = D_a + self.X_Ia
    #     X_center = XD_Ia[1,:]
    #
    #     rotation_axes = np.array([[1, 0, 0]], dtype=np.float_)
    #     rotation_angles = np.array([-theta], dtype=np.float_)
    #     rotation_centers = np.array([X_center], dtype=np.float_)
    #
    #     x_single = np.array([XD_Ia], dtype='f')
    #     x_pulled_back = x_single - rotation_centers[:, np.newaxis, :]
    #     q = axis_angle_to_q(rotation_axes, rotation_angles)
    #     x_rotated = qv_mult(q, x_pulled_back)
    #     x_pushed_forward = x_rotated + rotation_centers[:, np.newaxis, :]
    #     x_translated = x_pushed_forward #  + self.translations[:, np.newaxis, :]
    #     return x_translated[0,...]

    delta_x = tr.Property(depends_on='+GEO')
    @tr.cached_property
    def _get_delta_x(self):
        return self.symb.get_delta_x()

    delta_phi = tr.Property(depends_on='+GEO')
    @tr.cached_property
    def _get_delta_phi(self):
        return self.symb.get_delta_phi()

    R_0 = tr.Property(depends_on='+GEO')
    @tr.cached_property
    def _get_R_0(self):
        return self.symb.get_R_0()
Esempio n. 7
0
class WBCell5Param(WBCell, bu.InjectSymbExpr):
    name = 'waterbomb cell 5p'
    symb_class = WBCellSymb5ParamXL

    plot_backend = 'k3d'

    gamma = bu.Float(1, GEO=True)
    x_ur = bu.Float(1000, GEO=True)
    a = bu.Float(1000, GEO=True)
    b = bu.Float(1000, GEO=True)
    c = bu.Float(1000, GEO=True)
    a_low = bu.Float(2000)
    b_low = bu.Float(2000)
    c_low = bu.Float(2000)
    a_high = bu.Float(2000)
    b_high = bu.Float(2000)
    c_high = bu.Float(2000)
    y_sol1 = bu.Bool(False, GEO=True)
    x_sol1 = bu.Bool(False, GEO=True)

    continuous_update = True

    ipw_view = bu.View(
        bu.Item('gamma',
                latex=r'\gamma',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high=np.pi / 2,
                    n_steps=101,
                    continuous_update=continuous_update)),
        bu.Item('x_ur',
                latex=r'x^\urcorner',
                editor=bu.FloatRangeEditor(
                    low=-2000,
                    high=3000,
                    n_steps=101,
                    continuous_update=continuous_update)),
        bu.Item('a',
                latex='a',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high_name='a_high',
                    n_steps=101,
                    continuous_update=continuous_update)),
        bu.Item('b',
                latex='b',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high_name='b_high',
                    n_steps=101,
                    continuous_update=continuous_update)),
        bu.Item('c',
                latex='c',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high_name='c_high',
                    n_steps=101,
                    continuous_update=continuous_update)),
        bu.Item('y_sol1'),
        bu.Item('x_sol1'),
        *WBCell.ipw_view.content,
    )

    n_I = tr.Property

    def _get_n_I(self):
        return len(self.X_Ia)

    X_Ia = tr.Property(depends_on='+GEO')
    '''Array with nodal coordinates I - node, a - dimension
    '''

    @tr.cached_property
    def _get_X_Ia(self):
        gamma = self.gamma
        alpha = np.pi / 2 - gamma

        P_1 = self.symb.get_P_1()
        P_2 = self.symb.get_P_2()
        P_3 = self.symb.get_P_3()

        #        print('P', P_1, P_2, P_3, P_1*P_2*P_3)

        x_ur = self.x_ur

        if self.y_sol1:
            A = self.symb.get_A1_()
            B = self.symb.get_B1_()
            C = self.symb.get_C1_()
            if self.x_sol1:
                x_ul = self.symb.get_x_ul11_(A, B, C)
            else:
                x_ul = self.symb.get_x_ul12_(A, B, C)
            y_ul = self.symb.get_y_ul1_(x_ul)
            y_ur = self.symb.get_y_ur1_(x_ul)
        else:
            A = self.symb.get_A2_()
            B = self.symb.get_B2_()
            C = self.symb.get_C2_()
            if self.x_sol1:
                x_ul = self.symb.get_x_ul21_(A, B, C)
            else:
                x_ul = self.symb.get_x_ul22_(A, B, C)
            y_ul = self.symb.get_y_ul2_(x_ul)
            y_ur = self.symb.get_y_ur2_(x_ul)

        z_ur = self.symb.get_z_ur_(x_ul)
        z_ul = self.symb.get_z_ul_(x_ul)

        x_ll = -x_ur
        x_lr = -x_ul
        y_ll = -y_ur
        y_lr = -y_ul
        z_ll = z_ur
        z_lr = z_ul

        V_r_1 = self.symb.get_V_r_1().flatten()
        V_l_1 = self.symb.get_V_l_1().flatten()

        return np.array(
            [
                [0, 0, 0],  # 0 point
                [x_ur, y_ur, z_ur],  #U++
                [x_ul, y_ul, z_ul],  #U-+  ul
                [x_lr, y_lr, z_lr],  #U+-
                [x_ll, y_ll, z_ll],  #U--
                V_r_1,
                V_l_1,
            ],
            dtype=np.float_)

    I_boundary = tr.Array(np.int_, value=[
        [2, 1],
        [6, 5],
        [4, 3],
    ])
    '''Boundary nodes in 2D array to allow for generation of shell boundary nodes'''

    X_theta_Ia = tr.Property(depends_on='+GEO')
    '''Array with nodal coordinates I - node, a - dimension
    '''

    @tr.cached_property
    def _get_X_theta_Ia(self):
        D_a = self.symb.get_D_(self.alpha).T
        theta = self.symb.get_theta_sol(self.alpha)
        XD_Ia = D_a + self.X_Ia
        X_center = XD_Ia[1, :]

        rotation_axes = np.array([[1, 0, 0]], dtype=np.float_)
        rotation_angles = np.array([-theta], dtype=np.float_)
        rotation_centers = np.array([X_center], dtype=np.float_)

        x_single = np.array([XD_Ia], dtype='f')
        x_pulled_back = x_single - rotation_centers[:, np.newaxis, :]
        q = axis_angle_to_q(rotation_axes, rotation_angles)
        x_rotated = qv_mult(q, x_pulled_back)
        x_pushed_forward = x_rotated + rotation_centers[:, np.newaxis, :]
        x_translated = x_pushed_forward  #  + self.translations[:, np.newaxis, :]
        return x_translated[0, ...]

    delta_x = tr.Property(depends_on='+GEO')

    @tr.cached_property
    def _get_delta_x(self):
        return self.symb.get_delta_x()

    delta_phi = tr.Property(depends_on='+GEO')

    @tr.cached_property
    def _get_delta_phi(self):
        return self.symb.get_delta_phi()

    R_0 = tr.Property(depends_on='+GEO')

    @tr.cached_property
    def _get_R_0(self):
        return self.symb.get_R_0()