class CustomShape(CrossSectionShapeBase):
    cs_points_str = Str(
        '100, 0\n100, 140\n25, 150\n25, 750\n100, 760\n100, 900\n-100, 900\n-100, 760\n-25, 750\n-25, 150\n-100, 140\n-100, 0'
    )
    apply_points_btn = Button()

    @tr.observe("apply_points_btn")
    def apply_points_btn_clicked(self, event):
        print(
            'This should update the plot with the new points, but maybe it\'s not needed'
        )

    ipw_view = View(
        Item('cs_points_str',
             latex=r'\mathrm{Points}',
             editor=TextAreaEditor()),
        Item('apply_points_btn',
             editor=ButtonEditor(label='Apply points', icon='refresh')),
    )

    cs_points = tr.Property()

    def _get_cs_points(self):
        return self._parse_2d_points_str_into_array(self.cs_points_str)

    @staticmethod
    def _parse_2d_points_str_into_array(points_str):
        """ This will parse str of points written like this '0, 0\n 1, 1' to ([0, 0], [1, 1]) """
        points_str = points_str.replace('\n', ', ').replace('\r', ', ')
        points_array = np.fromstring(points_str, dtype=int, sep=',')
        if points_array.size % 2 != 0:
            points_array = np.append(points_array, 0)
        points_array = points_array.reshape((int(points_array.size / 2), 2))
        return points_array.reshape((int(points_array.size / 2), 2))

    def get_cs_area(self):
        points_xy = self.cs_points
        x = points_xy[:, 0]
        y = points_xy[:, 1]
        # See https://stackoverflow.com/a/30408825 for following Implementation of Shoelace formula
        return 0.5 * np.abs(
            np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))

    def get_cs_i(self):
        pass

    def get_b(self, z_positions_array):
        # TODO get b for polygon given its points coordinates
        pass

    def update_plot(self, ax):
        cs = Polygon(self.cs_points)
        patch_collection = PatchCollection([cs],
                                           facecolor=(.5, .5, .5, 0.2),
                                           edgecolors=(0, 0, 0, 1))
        ax.add_collection(patch_collection)
        # ax.scatter(0, CustomShape.get_cs_i(self)[0], color='white', s=self.B_w, marker="+")

        ax.autoscale_view()
        ax.set_aspect('equal')
class CarbonReinfMatMod(ReinfMatMod, bu.InjectSymbExpr):

    name = 'Carbon'

    symb_class = CarbonReinfMatModSymbExpr

    E = bu.Float(200000, MAT=True, desc='E modulus of carbon')
    f_t = bu.Float(2000, MAT=True, desc='carbon breaking stress')

    eps_cr = tr.Property(bu.Float, depends_on='+MAT')

    @tr.cached_property
    def _get_eps_cr(self):
        return self.f_t / self.E

    ipw_view = bu.View(
        bu.Item('factor'),
        bu.Item('E', latex=r'E \mathrm{[N/mm^{2}]}'),
        bu.Item('f_t', latex=r'f_\mathrm{t} \mathrm{[N/mm^{2}]}'),
        bu.Item('eps_cr', latex=r'\varepsilon_\mathrm{cr} \mathrm{[-]}'),
    )

    def get_eps_plot_range(self):
        return np.linspace(-0.1 * self.eps_cr, 1.1 * self.eps_cr, 300)

    def get_sig(self, eps):
        # TODO: factor should be applied only to strength in case of steel/carbon according to EC2
        temp = self.f_t
        self.f_t *= self.factor
        sig = self.symb.get_sig(eps)
        self.f_t = temp
        return sig
class SteelReinfMatMod(ReinfMatMod, bu.InjectSymbExpr):
    name = 'Steel'

    symb_class = SteelReinfMatModSymbExpr

    E_s = bu.Float(200000, MAT=True, desc='E modulus of steel')
    f_sy = bu.Float(500, MAT=True, desc='steel yield stress')
    eps_ud = bu.Float(0.025, MAT=True, desc='steel failure strain')

    eps_sy = tr.Property(bu.Float, depends_on='+MAT')

    @tr.cached_property
    def _get_eps_sy(self):
        return self.f_sy / self.E_s

    ipw_view = bu.View(
        bu.Item('factor'),
        bu.Item('E_s', latex=r'E_\mathrm{s} \mathrm{[N/mm^{2}]}'),
        bu.Item('f_sy', latex=r'f_\mathrm{sy} \mathrm{[N/mm^{2}]}'),
        bu.Item('eps_ud', latex=r'\varepsilon_\mathrm{ud} \mathrm{[-]}'),
        bu.Item('eps_sy',
                latex=r'\varepsilon_\mathrm{sy} \mathrm{[-]}',
                readonly=True),
    )

    def get_eps_plot_range(self):
        return np.linspace(-1.1 * self.eps_ud, 1.1 * self.eps_ud, 300)

    def get_sig(self, eps):
        temp = self.f_sy
        self.f_sy *= self.factor
        sig = self.symb.get_sig(eps)
        self.f_sy = temp
        return sig
class FormingProcessView(tr.HasStrictTraits):
    '''Forming process viewer with task tree editor
    '''

    forming_process = tr.Instance(FormingProcess)
    root = tr.Property(tr.Instance(FormingTask))
    '''All FormingTask steps.
    '''
    def _get_root(self):
        return self.forming_process.factory_task

    selected = tr.Instance(IFormingTask)

    view1 = View(VSplit(Group(
        Item('root',
             editor=FormingTask_tree_editor,
             resizable=True,
             show_label=False),
        label='Design tree',
        scrollable=True,
    ),
                        Group(UItem('selected@')),
                        dock='tab'),
                 dock='tab',
                 resizable=True,
                 title='Forming Process View',
                 width=1.0,
                 height=1.0)
class CrossSection(BMCSLeafNode, RInputRecord):
    '''Parameters of the pull-out cross section
    '''
    node_name = 'cross-section'

    R_m = tr.Float(20,
                   CS=True,
                   input=True,
                   unit=r'$\mathrm{mm}$',
                   symbol=r'R_\mathrm{m}',
                   auto_set=False,
                   enter_set=True,
                   desc='matrix area')
    R_f = tr.Float(1.0,
                   CS=True,
                   input=True,
                   unit='$\\mathrm{mm}$',
                   symbol='R_\mathrm{f}',
                   auto_set=False,
                   enter_set=True,
                   desc='reinforcement area')
    P_b = tr.Property(unit='$\\mathrm{mm}$',
                      symbol='p_\mathrm{b}',
                      desc='perimeter of the bond interface',
                      depends_on='R_f')

    @tr.cached_property
    def _get_P_b(self):
        return 2 * np.pi * self.R_f

    view = ui.View(ui.Item('R_m'), ui.Item('R_f'),
                   ui.Item('P_b', style='readonly'))

    tree_view = view
Beispiel #6
0
class CrossSectionDesign(Model):
    name = 'Cross Section Design'

    matrix = EitherType(
        options=[
            ('piecewise linear', PWLConcreteMatMod),
            ('EC2', EC2ConcreteMatMod),
            ('EC2 with plateau', EC2PlateauConcreteMatMod),
            # ('EC2 softening tension', ConcreteMaterialModelAdv),
        ],
        MAT=True)

    cross_section_layout = Instance(CrossSectionLayout)

    def _cross_section_layout_default(self):
        return CrossSectionLayout(cs_design=self)

    tree = ['matrix', 'cross_section_layout', 'cross_section_shape']

    csl = tr.Property

    def _get_csl(self):
        return self.cross_section_layout

    H = tr.Property(Float)

    def _get_H(self):
        return self.cross_section_shape_.H

    def _set_H(self, value):
        self.cross_section_shape_.H = value

    cross_section_shape = EitherType(
        options=[
            ('rectangle', Rectangle),
            # ('circle', Circle),
            ('I-shape', IShape),
            ('T-shape', TShape),
            ('custom', CustomShape)
        ],
        CS=True,
        tree=True)

    ipw_view = View(
        Item('matrix',
             latex=r'\mathrm{concrete behavior}',
             editor=EitherTypeEditor(show_properties=False)),
        Item('cross_section_shape',
             latex=r'\mathrm{shape}',
             editor=EitherTypeEditor(show_properties=False)),
        Item('cross_section_layout', latex=r'\mathrm{layout}'),
    )

    def subplots(self, fig):
        return fig.subplots(1, 1)

    def update_plot(self, ax):
        self.cross_section_shape_.update_plot(ax)
        self.cross_section_layout.update_plot(ax)
Beispiel #7
0
class Conjoint(Model):
    # The imput data for calculation
    owner_ref = _traits.WeakRef()
    # design = DataSet()
    design = _traits.DelegatesTo('owner_ref')
    design_vars = _traits.List(_traits.Str())
    liking = DataSet()
    # consumers = DataSet()
    consumers = _traits.DelegatesTo('owner_ref')
    consumers_vars = _traits.List(_traits.Str())

    # Conjoint settings
    model_struct = _traits.Enum('Struct 1', 'Struct 2', 'Struct 3')

    # Conjoint calculation state
    ccs = _traits.Instance(ConjointCalcState, ())
    cm = _traits.Instance(ConjointMachine)

    # depends_on
    res = _traits.Property(
        depends_on='design_vars, consumers_vars, model_struct')

    def _cm_default(self):
        try:
            return ConjointMachine()
        except RNotFoundException:
            self.ccs.messages = ("Was not able to find and start R.\n"
                                 "You have to check the installation of R")
            self.ccs.edit_traits(kind='livemodal')

    @_traits.on_trait_change('owner_ref.model_struct')
    def _struc_altered(self, new):
        self.model_struct = new

    @_traits.on_trait_change('owner_ref.sel_design_var')
    def _des_var_altered(self, new):
        self.design_vars = new

    @_traits.on_trait_change('owner_ref.sel_cons_char')
    def _cons_char_altered(self, new):
        self.consumers_vars = new

    @_traits.cached_property
    def _get_res(self):
        if not self.cm.run_state:
            self.cm.run_state = self.ccs

        model = {
            'Struct 1': 1,
            'Struct 2': 2,
            'Struct 3': 3
        }[self.model_struct]

        self.cm.schedule_calculation(self.design, sorted(self.design_vars),
                                     self.liking, model, self.consumers,
                                     sorted(self.consumers_vars))
        self.ccs.edit_traits(kind='livemodal')
        return self.cm.get_result()
class Geo(tr.HasStrictTraits):

    eta = tr.List([eta_0, eta_1, eta_2])

    R_r = tr.List([sp.sin(eta_0) * eta_0, sp.cos(eta_0 + 1)**2, eta_1])

    def F_r(self, *eta_):
        r = np.array([
            re.subs({e: et
                     for e, et in zip(self.eta, eta_)}) for re in self.R_r
        ],
                     dtype=np.float_)
        return np.einsum('ij,jk->ki', r[:, np.newaxis], np.identity(3))

    M_ra = tr.Property(depends_on='R_r, R_r_items')

    @tr.cached_property
    def _get_M_ra(self):
        return [[sp.diff(r, e) for r in self.R_r] for e in self.eta]

    def F_m(self, *eta_):
        return np.array(
            [[m.subs({e: et
                      for e, et in zip(self.eta, eta_)}) for m in m_a]
             for m_a in self.M_ra],
            dtype=np.float_)

    def orthogonal_base(self, *eta_):
        m_01 = self.F_m(*eta_)
        m_2 = np.einsum('...i,...j,ijk->...k', m_01[0, :], m_01[1, :], EPS)
        m_1a = np.einsum('...i,...j,ijk->...k', m_2, m_01[0, :], EPS)
        m = np.array([m_01[0, :], m_1a, m_2])
        m1 = m / np.sqrt(np.einsum('ij,ij->i', m, m))[:, np.newaxis]
        return m1

    def plot_basis(self, *eta):
        r = self.F_r(*eta)
        m = self.orthogonal_base(*eta)
        rm = r + m
        mp = np.vstack([r[np.newaxis, ...], rm[np.newaxis, ...]])
        mlab.plot3d(mp[:, 0, 0],
                    mp[:, 0, 1],
                    mp[:, 0, 2],
                    color=black,
                    tube_radius=0.01)
        mlab.plot3d(mp[:, 1, 0],
                    mp[:, 1, 1],
                    mp[:, 1, 2],
                    color=black,
                    tube_radius=0.01)
        mlab.plot3d(mp[:, 2, 0],
                    mp[:, 2, 1],
                    mp[:, 2, 2],
                    color=black,
                    tube_radius=0.01)
        mlab.text3d(rm[0, 0], rm[0, 1], rm[0, 2], 'X', color=black, scale=0.1)
        mlab.text3d(rm[1, 0], rm[1, 1], rm[1, 2], 'Y', color=black, scale=0.1)
        mlab.text3d(rm[2, 0], rm[2, 1], rm[2, 2], 'Z', color=black, scale=0.1)
Beispiel #9
0
class EC2PlateauConcreteMatMod(EC2ConcreteMatModBase, bu.InjectSymbExpr):
    name = 'EC2 Concrete with Plateau'

    symb_class = EC2PlateauConcreteMatModSymbExpr

    f_cm = bu.Float(28)

    f_cd = tr.Property(desc='Design compressive strength of concrete',
                       MAT=True)

    def _get_f_cd(self):
        if self.factor == 1:
            return self.f_cm
        else:
            return EC2.get_f_cd(self.f_ck, factor=self.factor)

    f_ck = tr.Property(desc='Characteristic compressive strength of concrete',
                       MAT=True)

    def _get_f_ck(self):
        return EC2.get_f_ck_from_f_cm(self.f_cm)

    n = tr.Property(desc='Exponent used in EC2, eq. 3.17', MAT=True)

    def _get_n(self):
        return EC2.get_n(self.f_ck)

    eps_cy = tr.Property(desc='Matrix compressive yield strain', MAT=True)

    def _get_eps_cy(self):
        return -EC2.get_eps_c2(self.f_ck)

    eps_cu = tr.Property(desc='Ultimate matrix compressive strain', MAT=True)

    def _get_eps_cu(self):
        return -EC2.get_eps_cu2(self.f_ck)

    def get_sig(self, eps):
        sig = self.symb.get_sig(eps)
        # Compression branch is scaled when defining f_cd
        sig_with_scaled_tension_branch = np.where(sig > 0, self.factor * sig,
                                                  sig)
        return sig_with_scaled_tension_branch

    ipw_view = bu.View(*EC2ConcreteMatModBase.ipw_view.content, )
Beispiel #10
0
class DescStatBasePlot(BasePlot):
    ds = _traits.Instance(DataSet)
    plot_data = _traits.Property()
    """The data set that is to be shown i table view of the plot data"""
    def _get_plot_data(self):
        nds = copy.deepcopy(self.ds)
        df = self.ds.mat.transpose()
        nds.mat = df.sort_index(axis=0, ascending=False)
        return nds
Beispiel #11
0
class Model(BMCSTreeNode):
    '''Contains the primary unknowns variables U_k
    '''
    tstep_type = tr.Type(TStep)
    tloop_type = tr.Type(TLoop)
    hist_type = tr.Type(Hist)
    sim_type = tr.Type(SimControler)

    tstep = tr.Property(depends_on='tstep_type')

    @tr.cached_property
    def _get_tstep(self):
        return self.tstep_type(model=self)

    tloop = tr.Property(depends_on='tloop_type')

    @tr.cached_property
    def _get_tloop(self):
        return self.tloop_type(tstep=self.tstep)

    hist = tr.Property(depends_on='hist_type')

    @tr.cached_property
    def _get_hist(self):
        return self.hist_type(model=self)

    sim = tr.Property(depends_on='sim_type')

    @tr.cached_property
    def _get_sim(self):
        return self.sim_type(model=self)

    bc = tr.List(tr.Callable)

    U_shape = tr.Tuple(1,)

    def init_state(self):
        self.U_k = np.zeros(self.U_shape, dtype=np.float)
        self.U_n = np.copy(self.U_n)
        self.hist.init_state()

    def get_plot_sheet(self):
        return

    U_k = tr.Array(np.float_, TRIAL_STATE=True)
    U_n = tr.Array(np.float_, FUND_STATE=True)

    S = tr.Dict(tr.Str, tr.Array(np.float), STATE=True)

    F = tr.Property(depends_on='+TRIAL_STATE,+INPUT')

    @tr.cached_property
    def _get_F(self):
        raise NotImplemented

    d_F_U = tr.Property(depends_on='+TRIAL_STATE,+INPUT')

    @tr.cached_property
    def _get_d_F_U(self):
        raise NotImplemented
Beispiel #12
0
class Viz3D(tr.HasTraits):

    vis3d = tr.WeakRef

    name = tr.Property()

    def _get_name(self):
        return self.vis3d.var

    def setup(self):
        pass

    def plot(self, vot):
        pass
Beispiel #13
0
class TimeFunction(bu.InteractiveModel):
    name = 'Time function'
    n_i = bu.Int(0,
                 input=True,
                 desc='number of data points between data points')
    '''Number of steps between two values
    '''

    values = tr.Array(np.float_, value=[0, 1])
    '''Values of the time function.
    '''

    ipw_view = bu.View(bu.Item('n_i', latex=r'n_i', minmax=(0, 100)))

    t_step = tr.Property(tr.Float, depends_on='+input')
    '''Step size.
    '''
    @tr.cached_property
    def _get_t_step(self):
        n_values = len(self.values)
        return (self.n_i + 1) * n_values

    tf = tr.Property(depends_on='data_points')

    def _get_tf(self):
        n_values = len(self.values)
        t_values = np.linspace(0, 1, n_values)
        return interpolate.interp1d(t_values, self.values)

    def __call__(self, arg):
        return self.tf(arg)

    def update_plot(self, ax):
        n_values = len(self.values)
        t = np.linspace(0, 1, (self.n_i) * (n_values - 1) + n_values)
        v = self.tf(t)
        ax.plot(t, v, '-o')
class MSIntegScheme(Model):
    """Interface of an integration scheme
    """

    MPNN = tr.Property(depends_on='n_mp')
    """Get the operator of the microplane normals
    """
    @tr.cached_property
    def _get_MPNN(self):
        MPNN_nij = np.einsum('ni,nj->nij', self.MPN, self.MPN)
        return MPNN_nij

    MPTT = tr.Property(depends_on='n_mp')
    """Rank three tangential tensor (operator) for each microplane
    """

    @tr.cached_property
    def _get_MPTT(self):
        delta = np.identity(3)
        MPTT_nijr = 0.5 * (
            np.einsum('ni,jr -> nijr', self.MPN, delta) +
            np.einsum('nj,ir -> njir', self.MPN, delta) -
            2 * np.einsum('ni,nj,nr -> nijr', self.MPN, self.MPN, self.MPN))
        return MPTT_nijr
Beispiel #15
0
class Viz3DField(Viz3D):

    visible = tr.Property(tr.Bool)

    def _set_visible(self, visible):
        self.d.visible = visible

    def _get_visible(self):
        return self.d.visible

    def plot(self, vot):
        # self.vis3d.sim
        ts = self.vis3d.tstep
        idx = ts.hist.get_time_idx(vot)
        self.d.file_list = self.vis3d.file_list
        self.d.timestep = idx
Beispiel #16
0
class HCFFRoot(HCFFParent):

    #name = tr.Str('root')

    import_manager = tr.Instance(FileImportManager, ())

    plot_options = tr.List(PlotOptions)

    output_npy = tr.Property()

    tree_view = ui.View(
        ui.VGroup(
            ui.Item('import_manager', style='custom', show_label=False)
        )
    )

    traits_view = tree_view
Beispiel #17
0
class BasePlot(_chaco.DataView):

    # Copy from chaco.plot
    # The title of the plot.
    title = _traits.Property()

    # Use delegates to expose the other PlotLabel attributes of the plot title
    title_text = _traits.Delegate("_title", prefix="text", modify=True)

    # The PlotLabel object that contains the title.
    _title = _traits.Instance(_chaco.PlotLabel)

    def __init__(self, *args, **kwargs):
        super(BasePlot, self).__init__(*args, **kwargs)
        self._init_title()

    def _init_title(self):
        self._title = _chaco.PlotLabel(font="swiss 16",
                                       visible=False,
                                       overlay_position="top",
                                       component=self)

    def __title_changed(self, old, new):
        self._overlay_change_helper(old, new)

    def _set_title(self, text):
        self._title.text = text
        if text.strip() != "":
            self._title.visible = True
        else:
            self._title.visible = False

    def _get_title(self):
        return self._title.text

    def get_plot_name(self):
        return self.plot_data.display_name

    def export_image(self, fname, size=sz_plot_img):
        """Save plot as png image."""
        # self.outer_bounds = list(size)
        # self.do_layout(force=True)
        gc = _chaco.PlotGraphicsContext(self.outer_bounds)
        gc.render_component(self)
        gc.save(fname, file_format=None)
Beispiel #18
0
class PointCloud(Vis3D):
    '''State object
    '''
    p = tr.Tuple
    '''Point positions
    '''
    def _p_default(self):
        x, y, z, s = np.random.random((4, 100))
        return x, y, z, s

    points = tr.Property(tr.Tuple)

    def _get_points(self):
        x, y, z, s = self.p
        c = self.vot
        return x * c, y * c, z * c, s * c

    viz3d_classes = dict(default=PointCloudViz3D,
                         something_else=PointCloudViz3D)
Beispiel #19
0
class TimeFunction(Model):
    name = 'time function'
    t_max = Float(1.0, TIME=True)
    t_shift = Float(0.0, TIME=True)

    def __call__(self, arg):
        return self.time_function(arg)

    time_function = tr.Property(depends_on='state_changed')

    @tr.cached_property
    def _get_time_function(self):
        return self._generate_time_function()

    def update_plot(self, axes):
        t_range = np.linspace(0, self.t_max, 500)
        f_range = self.time_function(t_range)
        axes.plot(t_range, f_range)
        axes.set_xlabel(r'$t$ [-]')
        axes.set_ylabel(r'$\theta$ [-]')
Beispiel #20
0
class PlotBase(_chaco.Plot):
    # The data set to return when plot window asks for the data to
    # show in table form
    # I have to override this property as well when i override the getter
    plot_data = _traits.Property()
    # The function to call if the plot is double clicked
    ld_action_func = _traits.Callable()
    # Add extra room for y axis
    padding_left = 75

    def add_left_down_action(self, left_down_action_func):
        self.ld_action_func = left_down_action_func

    def normal_left_down(self, mouse_event):
        if self.ld_action_func:
            self.ld_action_func()

    def _get_plot_data(self):
        raise NotImplementedError('plot_data getter is not implemented')

    def get_plot_name(self):
        return self.plot_data.display_name
Beispiel #21
0
class TFSelector(TimeFunction):
    name = 'time function'
    profile = EitherType(options=[
        ('monotonic', TFMonotonic), ('bilinear', TFBilinear),
        ('cyclic-sym-incr', TFCyclicSymmetricConstant),
        ('cyclic-sym-const', TFCyclicSymmetricIncreasing),
        ('cyclic-nonsym-incr', TFCyclicNonsymmetricConstant),
        ('cyclic-nonsym-const', TFCyclicNonsymmetricIncreasing)
    ],
                         TIME=True)

    t_max = tr.Property(Float)

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

    ipw_view = View(Item('profile'))

    def __call__(self, arg):
        return self.profile_(arg)

    def update_plot(self, axes):
        self.profile_.update_plot(axes)
class WBTessellation4P(bu.Model):
    name = 'WB Tessellation 4P'

    wb_cell = bu.Instance(WBCell4Param)

    def _wb_cell_default(self):
        wb_cell = WBCell4Param()
        self.update_wb_cell_params(wb_cell)
        return wb_cell

    tree = ['wb_cell']

    plot_backend = 'k3d'

    n_phi_plus = bu.Int(5, GEO=True)
    n_x_plus = bu.Int(3, GEO=True)
    gamma = bu.Float(1.25, GEO=True)
    a = bu.Float(1000, GEO=True)
    a_high = bu.Float(2000)
    b = bu.Float(1000, GEO=True)
    b_high = bu.Float(2000)
    c = bu.Float(1000, GEO=True)
    c_high = bu.Float(2000)

    show_wireframe = bu.Bool(True, GEO=True)
    show_nodes = bu.Bool(False, GEO=True)
    show_node_labels = bu.Bool(False, GEO=True)
    WIREFRAME = 'k3d_mesh_wireframe'
    NODES = 'k3d_nodes'
    NODES_LABELS = 'k3d_nodes_labels'

    @tr.observe('+GEO', post_init=True)
    def update_wb_cell(self, event):
        self.update_wb_cell_params(self.wb_cell)

    def update_wb_cell_params(self, wb_cell):
        wb_cell.trait_set(
            gamma=self.gamma,
            a=self.a,
            a_high=self.a_high,
            b=self.b,
            b_high=self.b_high,
            c=self.c,
            c_high=self.c_high,
        )

    ipw_view = bu.View(
        # bu.Item('wb_cell'),
        *WBCell4Param.ipw_view.content,
        bu.Item('n_phi_plus', latex=r'n_\phi'),
        bu.Item('n_x_plus', latex=r'n_x'),
        # bu.Item('show_wireframe'),
        # bu.Item('show_node_labels'),
        bu.Item('show_nodes'),
    )

    def get_phi_range(self, delta_phi):
        return np.arange(-(self.n_phi_plus - 1), self.n_phi_plus) * delta_phi

    def get_X_phi_range(self, delta_phi, R_0):
        """Given an array of angles and radius return an array of coordinates
        """
        phi_range = self.get_phi_range((delta_phi))
        return np.array([
            np.fabs(R_0) * np.sin(phi_range),
            np.fabs(R_0) * np.cos(phi_range) + R_0
        ]).T

    def get_X_x_range(self, delta_x):
        return np.arange(-(self.n_x_plus - 1), self.n_x_plus) * delta_x

    cell_map = tr.Property

    def _get_cell_map(self):
        delta_x = self.wb_cell.delta_x
        delta_phi = self.wb_cell.delta_phi
        R_0 = self.wb_cell.R_0

        X_x_range = self.get_X_x_range(delta_x)
        X_phi_range = self.get_X_phi_range(delta_phi, R_0)
        n_idx_x = len(X_x_range)
        n_idx_phi = len(X_phi_range)
        idx_x = np.arange(n_idx_x)
        idx_phi = np.arange(n_idx_phi)

        idx_x_ic = idx_x[(n_idx_x) % 2::2]
        idx_x_id = idx_x[(n_idx_x + 1) % 2::2]
        idx_phi_ic = idx_phi[(n_idx_phi) % 2::2]
        idx_phi_id = idx_phi[(n_idx_phi + 1) % 2::2]

        n_ic = len(idx_x_ic) * len(idx_phi_ic)
        n_id = len(idx_x_id) * len(idx_phi_id)

        n_cells = n_ic + n_id
        return n_cells, n_ic, n_id, idx_x_ic, idx_x_id, idx_phi_ic, idx_phi_id

    n_cells = tr.Property

    def _get_n_cells(self):
        n_cells, _, _, _, _, _, _ = self.cell_map
        return n_cells

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

    @tr.cached_property
    def _get_X_cells_Ia(self):

        delta_x = self.wb_cell.delta_x
        delta_phi = self.wb_cell.delta_phi
        R_0 = self.wb_cell.R_0

        X_Ia_wb_rot = np.copy(self.wb_cell.X_Ia)
        X_Ia_wb_rot[..., 2] -= R_0
        X_cIa = np.array([X_Ia_wb_rot], dtype=np.float_)
        rotation_axes = np.array([[1, 0, 0]], dtype=np.float_)
        rotation_angles = self.get_phi_range(delta_phi)
        q = axis_angle_to_q(rotation_axes, rotation_angles)
        X_dIa = qv_mult(q, X_cIa)
        X_dIa[..., 2] += R_0

        X_x_range = self.get_X_x_range(delta_x)
        X_phi_range = self.get_X_phi_range(delta_phi, R_0)
        n_idx_x = len(X_x_range)
        n_idx_phi = len(X_phi_range)
        idx_x = np.arange(n_idx_x)
        idx_phi = np.arange(n_idx_phi)

        idx_x_ic = idx_x[(n_idx_x) % 2::2]
        idx_x_id = idx_x[(n_idx_x + 1) % 2::2]
        idx_phi_ic = idx_phi[(n_idx_phi) % 2::2]
        idx_phi_id = idx_phi[(n_idx_phi + 1) % 2::2]

        X_E = X_x_range[idx_x_ic]
        X_F = X_x_range[idx_x_id]

        X_CIa = X_dIa[idx_phi_ic]
        X_DIa = X_dIa[idx_phi_id]

        expand = np.array([1, 0, 0])
        X_E_a = np.einsum('i,j->ij', X_E, expand)
        X_ECIa = X_CIa[np.newaxis, :, :, :] + X_E_a[:, np.newaxis,
                                                    np.newaxis, :]
        X_F_a = np.einsum('i,j->ij', X_F, expand)
        X_FDIa = X_DIa[np.newaxis, :, :, :] + X_F_a[:, np.newaxis,
                                                    np.newaxis, :]

        X_Ia = np.vstack(
            [X_ECIa.flatten().reshape(-1, 3),
             X_FDIa.flatten().reshape(-1, 3)])
        return X_Ia

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

    @tr.cached_property
    def _get_I_cells_Fi(self):
        I_Fi_cell = self.wb_cell.I_Fi
        n_I_cell = self.wb_cell.n_I
        n_cells = self.n_cells
        i_range = np.arange(n_cells) * n_I_cell
        I_Fi = (I_Fi_cell[np.newaxis, :, :] +
                i_range[:, np.newaxis, np.newaxis]).reshape(-1, 3)
        return I_Fi

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

    @tr.cached_property
    def _get_X_Ia(self):
        idx_unique, idx_remap = self.unique_node_map
        return self.X_cells_Ia[idx_unique]

    I_Fi = tr.Property(depends_on='+GEO')
    '''Facet - node mapping
    '''

    @tr.cached_property
    def _get_I_Fi(self):
        _, idx_remap = self.unique_node_map
        return idx_remap[self.I_cells_Fi]

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

    def _get_node_match_threshold(self):
        min_length = np.min([self.a, self.b, self.c])
        return min_length * 1e-4

    unique_node_map = tr.Property(depends_on='+GEO')
    '''Property containing the mapping between the crease pattern nodes
    with duplicate nodes and pattern with compressed nodes array.
    The criterion for removing a node is geometric, the threshold
    is specified in node_match_threshold.
    '''

    def _get_unique_node_map(self):
        # reshape the coordinates in array of segments to the shape (n_N, n_D
        x_0 = self.X_cells_Ia
        # construct distance vectors between every pair of nodes
        x_x_0 = x_0[:, np.newaxis, :] - x_0[np.newaxis, :, :]
        # calculate the distance between every pair of nodes
        dist_0 = np.sqrt(np.einsum('...i,...i', x_x_0, x_x_0))
        # identify those at the same location
        zero_dist = dist_0 < self.node_match_threshold
        # get their indices
        i_idx, j_idx = np.where(zero_dist)
        # take only the upper triangle indices
        upper_triangle = i_idx < j_idx
        idx_multi, idx_delete = i_idx[upper_triangle], j_idx[upper_triangle]
        # construct a boolean array with True at valid and False at deleted
        # indices
        idx_unique = np.ones((len(x_0), ), dtype='bool')
        idx_unique[idx_delete] = False
        # Boolean array of nodes to keep - includes both those that
        # are unique and redirection nodes to be substituted for duplicates
        idx_keep = np.ones((len(x_0), ), dtype=np.bool_)
        idx_keep[idx_delete] = False
        # prepare the enumeration map map
        ij_map = np.ones_like(dist_0, dtype=np.int_) + len(x_0)
        i_ = np.arange(len(x_0))
        # indexes of nodes that are being kept
        idx_row = i_[idx_keep]
        # enumerate the kept nodes by putting their number onto the diagonal
        ij_map[idx_keep, idx_keep] = np.arange(len(idx_row))
        # broadcast the substitution nodes into the interaction positions
        ij_map[i_idx, j_idx] = ij_map[i_idx, i_idx]
        # get the substitution node by picking up the minimum index within ac column
        idx_remap = np.min(ij_map, axis=0)

        return idx_unique, idx_remap

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

    @tr.cached_property
    def _get_I_CDij(self):
        n_cells, n_ic, n_id, _, x_cell_idx, _, y_cell_idx = self.cell_map
        x_idx, y_idx = x_cell_idx / 2, y_cell_idx / 2
        n_x_, n_y_ = len(x_idx), len(y_idx)
        I_cell_offset = (n_ic + np.arange(n_x_ * n_y_).reshape(
            n_x_, n_y_)) * self.wb_cell.n_I
        I_CDij_map = (I_cell_offset.T[:, :, np.newaxis, np.newaxis] +
                      self.wb_cell.I_boundary[np.newaxis, np.newaxis, :, :])
        return I_CDij_map

    def setup_plot(self, pb):
        self.pb = pb
        X_Ia = self.X_Ia.astype(np.float32)
        I_Fi = self.I_Fi.astype(np.uint32)

        I_M = self.I_CDij[(0, -1), :, (0, -1), :]
        _, idx_remap = self.unique_node_map
        J_M = idx_remap[I_M]
        X_Ma = X_Ia[J_M.flatten()]

        k3d_mesh = k3d.mesh(X_Ia, I_Fi, color=0x999999, side='double')

        pb.objects['k3d_mesh'] = k3d_mesh
        pb.plot_fig += k3d_mesh

        if self.show_nodes:
            self._add_nodes_to_fig(pb, X_Ma)

        if self.wb_cell.show_node_labels:
            self._add_nodes_labels_to_fig(pb, X_Ia)

        if self.show_wireframe:
            self._add_wireframe_to_fig(pb, X_Ia, I_Fi)

    def update_plot(self, pb):
        X_Ia = self.X_Ia.astype(np.float32)
        I_Fi = self.I_Fi.astype(np.uint32)

        I_M = self.I_CDij[(0, -1), :, (0, -1), :]
        _, idx_remap = self.unique_node_map
        J_M = idx_remap[I_M]
        X_Ma = X_Ia[J_M.flatten()]

        mesh = pb.objects['k3d_mesh']
        mesh.vertices = X_Ia
        mesh.indices = I_Fi

        if self.show_nodes:
            if self.NODES in pb.objects:
                pb.objects[self.NODES].positions = X_Ma
            else:
                self._add_nodes_to_fig(pb, X_Ma)
        else:
            if self.NODES in pb.objects:
                pb.clear_object(self.NODES)

        if self.show_wireframe:
            if self.WIREFRAME in pb.objects:
                wireframe = pb.objects[self.WIREFRAME]
                wireframe.vertices = X_Ia
                wireframe.indices = I_Fi
            else:
                self._add_wireframe_to_fig(pb, X_Ia, I_Fi)
        else:
            if self.WIREFRAME in pb.objects:
                pb.clear_object(self.WIREFRAME)

        if self.show_node_labels:
            if self.NODES_LABELS in pb.objects:
                pb.clear_object(self.NODES_LABELS)
            self._add_nodes_labels_to_fig(pb, X_Ia)
        else:
            if self.NODES_LABELS in pb.objects:
                pb.clear_object(self.NODES_LABELS)

    def _add_nodes_labels_to_fig(self, pb, X_Ia):
        text_list = []
        for I, X_a in enumerate(X_Ia):
            k3d_text = k3d.text('%g' % I,
                                tuple(X_a),
                                label_box=False,
                                size=0.8,
                                color=0x00FF00)
            pb.plot_fig += k3d_text
            text_list.append(k3d_text)
        pb.objects[self.NODES_LABELS] = text_list

    def _add_wireframe_to_fig(self, pb, X_Ia, I_Fi):
        k3d_mesh_wireframe = k3d.mesh(X_Ia,
                                      I_Fi,
                                      color=0x000000,
                                      wireframe=True)
        pb.plot_fig += k3d_mesh_wireframe
        pb.objects[self.WIREFRAME] = k3d_mesh_wireframe

    def _add_nodes_to_fig(self, pb, X_Ma):
        k3d_points = k3d.points(X_Ma, point_size=300)
        pb.objects[self.NODES] = k3d_points
        pb.plot_fig += k3d_points

    def _show_or_hide_fig_object(self, pb, show_obj, obj_name, obj_add_fun,
                                 obj_update_fun):
        if show_obj:
            if obj_name in pb.objects:
                obj_update_fun()
            else:
                obj_add_fun()
        else:
            if obj_name in pb.objects:
                pb.clear_object(obj_name)

    def export_fold_file(self, path=None):
        # See https://github.com/edemaine/fold/blob/master/doc/spec.md for fold file specification
        # Viewer: https://edemaine.github.io/fold/examples/foldviewer.html

        output_data = {
            "file_spec": 1,
            "file_creator": "BMCS software suite",
            "file_author": "RWTH Aachen - Institute of Structural Concrete",
            "file_title": "Preliminary Base",
            "file_classes": ["singleModel"],
            "frame_title": "Preliminary Base Crease Pattern",
            "frame_classes": ["creasePattern"],
            "vertices_coords": self.X_Ia.tolist(),
            "faces_vertices": self.I_Fi.tolist(),
            # To be completed
        }

        if path is None:
            path = time.strftime("%Y%m%d-%H%M%S") + '-shell.fold'

        with open(path, 'w') as outfile:
            json.dump(output_data, outfile, sort_keys=True, indent=4)
Beispiel #23
0
class ModelInteract(tr.HasTraits):

    models = tr.List([
    ])

    py_vars = tr.List(tr.Str)
    map_py2sp = tr.Dict

    d = tr.Float(0.03, GEO=True)
    h = tr.Float(0.8, GEO=True)

    # define the free parameters as traits with default, min and max values
    w_max = tr.Float(1.0)
    t = tr.Float(0.0001, min=1e-5, max=1)
    tau = tr.Float(0.5, interact=True)
    L_b = tr.Float(200, interact=True)
    E_f = tr.Float(100000, interact=True)
    A_f = tr.Float(20, interact=True)
    p = tr.Float(40, interact=True)
    E_m = tr.Float(26000, interact=True)
    A_m = tr.Float(100, interact=True)

    n_steps = tr.Int(50)

    sliders = tr.Property

    @tr.cached_property
    def _get_sliders(self):
        traits = self.traits(interact=True)
        vals = self.trait_get(interact=True)
        slider_names = self.py_vars[1:]
        max_vals = {name: getattr(traits, 'max', vals[name] * 2)
                    for name in slider_names}
        t_slider = {'t': ipw.FloatSlider(1e-5, min=1e-5, max=1, step=0.05,
                                         description=r'\(t\)')}
        param_sliders = {name: ipw.FloatSlider(value=vals[name],
                                               min=1e-5,
                                               max=max_vals[name],
                                               step=max_vals[name] /
                                               self.n_steps,
                                               description=r'\(%s\)' % self.map_py2sp[name].name)
                         for (name, _) in traits.items()
                         }
        t_slider.update(param_sliders)
        return t_slider

    w_range = tr.Property(tr.Array(np.float_), depends_on='w_max')

    @tr.cached_property
    def _get_w_range(self):
        return np.linspace(0, self.w_max, 50)

    x_range = tr.Property(tr.Array(np.float_), depends_on='L_b')

    @tr.cached_property
    def _get_x_range(self):
        return np.linspace(-self.L_b, 0, 100)

    model_plots = tr.Property(tr.List)

    @tr.cached_property
    def _get_model_plots(self):
        return [PlotModel(itr=self, model=m) for m in self.models]

    def init_fields(self):
        self.fig, ((self.ax_po, self.ax_u), (self.ax_eps, self.ax_tau)) = plt.subplots(
            2, 2, figsize=(9, 5), tight_layout=True
        )
        values = self.trait_get(interact=True)
        params = list(values[py_var] for py_var in self.py_vars[1:])
        for mp in self.model_plots:
            mp.init_fields(*params)
            mp.init_Pw(*params)
        self.ax_po.set_xlim(0, self.w_max * 1.05)

    def clear_fields(self):
        clear_plot(self.ax_po, self.ax_u, self.ax_eps, self.ax_tau)

    def update_fields(self, t, **values):
        w = t * self.w_max
        self.trait_set(**values)
        params = list(values[py_var] for py_var in self.py_vars[1:])
        L_b = self.L_b
        self.clear_fields()
        for mp in self.model_plots:
            mp.update_fields(w, *params)
            mp.update_Pw(w, *params)

        P_max = np.max(np.array([m.P_max for m in self.model_plots]))
        self.ax_po.set_ylim(0, P_max * 1.05)
        self.ax_po.set_xlim(0, self.w_max * 1.05)
        u_min = np.min(np.array([m.u_min for m in self.model_plots]))
        u_max = np.max(np.array([m.u_max for m in self.model_plots] + [1]))
        self.ax_u.set_ylim(u_min, u_max * 1.1)
        self.ax_u.set_xlim(xmin=-1.05 * L_b, xmax=0.05 * L_b)
        eps_min = np.min(np.array([m.eps_min for m in self.model_plots]))
        eps_max = np.max(np.array([m.eps_max for m in self.model_plots]))
        self.ax_eps.set_ylim(eps_min, eps_max * 1.1)
        self.ax_eps.set_xlim(xmin=-1.05 * L_b, xmax=0.05 * L_b)
        self.ax_tau.set_ylim(0, self.tau * 1.1)
        self.ax_tau.set_xlim(xmin=-1.05 * L_b, xmax=0.05 * L_b)
        self.fig.canvas.draw_idle()

    def set_w_max_fields(self, w_max):
        self.w_max = w_max
        values = {name: slider.value for name, slider in self.sliders.items()}
        self.update_fields(**values)

    def interact_fields(self):
        self.init_fields()
        self.on_w_max_change = self.update_fields
        sliders = self.sliders
        out = ipw.interactive_output(self.update_fields, sliders)
        self.widget_layout(out)

    #=========================================================================
    # Interaction on the pull-out curve spatial plot
    #=========================================================================
    def init_geometry(self):
        self.fig, (self.ax_po, self.ax_geo) = plt.subplots(
            1, 2, figsize=(8, 3.4))  # , tight_layout=True)
        values = self.trait_get(interact=True)
        params = list(values[py_var] for py_var in self.py_vars[1:])
        h = self.h
        x_C = np.array([[-1, 0], [0, 0], [0, h], [-1, h]], dtype=np.float_)
        self.line_C, = self.ax_geo.fill(*x_C.T, color='gray', alpha=0.3)
        for mp in self.model_plots:
            mp.line_aw, = self.ax_geo.fill([], [], color='white', alpha=1)
            mp.line_F, = self.ax_geo.fill([], [], color='black', alpha=0.8)
            mp.line_F0, = self.ax_geo.fill([], [], color='white', alpha=1)
            mp.init_Pw(*params)
        self.ax_po.set_xlim(0, self.w_max * 1.05)

    def clear_geometry(self):
        clear_plot(self.ax_po, self.ax_geo)

    def update_geometry(self, t, **values):
        w = t * self.w_max
        self.clear_geometry()
        self.trait_set(**values)
        params = list(values[py_var] for py_var in self.py_vars[1:])
        h = self.h
        d = self.d
        L_b = self.L_b
        f_top = h / 2 + d / 2
        f_bot = h / 2 - d / 2
        self.ax_geo.set_xlim(
            xmin=-1.05 * L_b, xmax=max(0.05 * L_b, 1.1 * self.w_max))
        x_C = np.array([[-L_b, 0], [0, 0], [0, h], [-L_b, h]], dtype=np.float_)
        self.line_C.set_xy(x_C)
        for mp in self.model_plots:
            a_val = mp.model.get_aw_pull(w, *params)
            width = d * 0.5
            x_a = np.array([[a_val, f_bot - width], [0, f_bot - width],
                            [0, f_top + width], [a_val, f_top + width]],
                           dtype=np.float_)
            mp.line_aw.set_xy(x_a)

            w_L_b = mp.model.get_w_L_b(w, *params)
            x_F = np.array([[-L_b + w_L_b, f_bot], [w, f_bot],
                            [w, f_top], [-L_b + w_L_b, f_top]], dtype=np.float_)
            mp.line_F.set_xy(x_F)
            x_F0 = np.array([[-L_b, f_bot], [-L_b + w_L_b, f_bot],
                             [-L_b + w_L_b, f_top], [-L_b, f_top]], dtype=np.float_)
            mp.line_F0.set_xy(x_F0)

            mp.update_Pw(w, *params)

        P_max = np.max(np.array([mp.P_max for mp in self.model_plots]))
        self.ax_po.set_ylim(0, P_max * 1.1)
        self.ax_po.set_xlim(0, self.w_max * 1.05)
        self.fig.canvas.draw_idle()

    def set_w_max(self, w_max):
        self.w_max = w_max
        values = {name: slider.value for name, slider in self.sliders.items()}
        self.on_w_max_change(**values)

    on_w_max_change = tr.Callable

    def interact_geometry(self):
        self.init_geometry()
        self.on_w_max_change = self.update_geometry
        sliders = self.sliders
        out = ipw.interactive_output(self.update_geometry, sliders)
        self.widget_layout(out)

    def widget_layout(self, out):
        sliders = self.sliders
        layout = ipw.Layout(grid_template_columns='1fr 1fr')
        param_sliders_list = [sliders[py_var] for py_var in self.py_vars[1:]]
        t_slider = sliders['t']
        grid = ipw.GridBox(param_sliders_list, layout=layout)
        w_max_text = ipw.FloatText(
            value=self.w_max,
            description=r'w_max',
            disabled=False
        )
        out_w_max = ipw.interactive_output(self.set_w_max,
                                           {'w_max': w_max_text})

        hbox = ipw.HBox([t_slider, w_max_text])
        box = ipw.VBox([hbox, grid, out, out_w_max])
        display(box)
Beispiel #24
0
class MATS3DMplCSDODF(MATS3DEval):
    '''Tangential constitutive law parameters
    '''
    gamma_T = Float(5000.,
                    label="Gamma",
                    desc=" Tangential Kinematic hardening modulus",
                    enter_set=True,
                    auto_set=False)

    K_T = Float(10.0,
                label="K",
                desc="Tangential Isotropic harening",
                enter_set=True,
                auto_set=False)

    S = Float(0.00001,
              label="S",
              desc="Damage strength",
              enter_set=True,
              auto_set=False)

    r = Float(1.2,
              label="r",
              desc="Damage cumulation parameter",
              enter_set=True,
              auto_set=False)

    c = Float(1.0,
              label="c",
              desc="Damage cumulation parameter",
              enter_set=True,
              auto_set=False)

    tau_pi_bar = Float(2.0,
                       label="Tau_bar",
                       desc="Reversibility limit",
                       enter_set=True,
                       auto_set=False)

    a = Float(0.0,
              label="a",
              desc="Lateral pressure coefficient",
              enter_set=True,
              auto_set=False)

    #-------------------------------------------
    # Normal_Tension constitutive law parameters
    #-------------------------------------------
    Ad = Float(10000.0,
               label="a",
               desc="brittleness coefficient",
               enter_set=True,
               auto_set=False)

    eps_0 = Float(56e-6,
                  label="a",
                  desc="threshold strain",
                  enter_set=True,
                  auto_set=False)

    eps_f = Float(250e-6,
                  label="a",
                  desc="threshold strain",
                  enter_set=True,
                  auto_set=False)

    #-----------------------------------------------
    # Normal_Compression constitutive law parameters
    #-----------------------------------------------
    K_N = Float(10000.,
                label="K_N",
                desc=" Normal isotropic harening",
                enter_set=True,
                auto_set=False)

    gamma_N = Float(15000.,
                    label="gamma_N",
                    desc="Normal kinematic hardening",
                    enter_set=True,
                    auto_set=False)

    sigma_0 = Float(20.,
                    label="sigma_0",
                    desc="Yielding stress",
                    enter_set=True,
                    auto_set=False)

    zeta_G = Float(1.0,
                   label="zeta_G",
                   desc="anisotropy parameter",
                   enter_set=True,
                   auto_set=False)

    state_var_shapes = tr.Property(tr.Dict(), depends_on='n_mp')
    '''Dictionary of state variable entries with their array shapes.
    '''
    @cached_property
    def _get_state_shapes(self):
        return {
            'w_N_Emn': (self.n_mp, ),
            'z_N_Emn': (self.n_mp, ),
            'alpha_N_Emn': (self.n_mp, ),
            'r_N_Emn': (self.n_mp, ),
            'eps_N_p_Emn': (self.n_mp, ),
            'sigma_N_Emn': (self.n_mp, ),
            'w_T_Emn': (self.n_mp, ),
            'z_T_Emn': (self.n_mp, ),
            'alpha_T_Emna': (self.n_mp, 3),
            'eps_T_pi_Emna': (self.n_mp, 3),
        }

    #--------------------------------------------------------------
    # microplane constitutive law (normal behavior CP + TD)
    #--------------------------------------------------------------
    def get_normal_law(self, eps_N_Emn, w_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn,
                       eps_N_p_Emn):

        E_N = self.E / (1.0 - 2.0 * self.nu)

        pos = eps_N_Emn > 1e-6
        H = 1.0 * pos

        sigma_n_trial = (1.0 - H * w_N_Emn) * E_N * (eps_N_Emn - eps_N_p_Emn)
        Z = self.K_N * r_N_Emn
        X = self.gamma_N * alpha_N_Emn

        h = self.sigma_0 + Z
        pos_iso = h > 1e-6
        f_trial = abs(sigma_n_trial - X) - h * pos_iso

        thres_1 = f_trial > 1e-6

        delta_lamda = f_trial / \
            (E_N + abs(self.K_N) + self.gamma_N) * thres_1
        eps_N_p_Emn = eps_N_p_Emn + delta_lamda * sign(sigma_n_trial - X)
        r_N_Emn = r_N_Emn + delta_lamda
        alpha_N_Emn = alpha_N_Emn + delta_lamda * sign(sigma_n_trial - X)

        #         def Z_N(z_N_Emn): return 1.0 / self.Ad * (-z_N_Emn) / (1.0 + z_N_Emn)
        #         Y_N = 0.5 * H * E_N * eps_N_Emn ** 2.0
        #         Y_0 = 0.5 * E_N * self.eps_0 ** 2.0
        #         f = Y_N - (Y_0 + Z_N(z_N_Emn))
        #
        #         thres_2 = f > 1e-6
        #
        #         def f_w(Y): return 1.0 - 1.0 / (1.0 + self.Ad * (Y - Y_0))
        #         w_N_Emn = f_w(Y_N) * thres_2
        #         z_N_Emn = - w_N_Emn * thres_2

        f_trial_Emn = eps_N_Emn - self.eps_0
        f_idx = np.where(f_trial_Emn > 0)
        z_N_Emn[f_idx] = eps_N_Emn[f_idx]
        w_N_Emn[f_idx] = (1. - (self.eps_0 / z_N_Emn[f_idx]) *
                          np.exp(-(z_N_Emn[f_idx] - self.eps_0) /
                                 (self.eps_f - self.eps_0)))

        sigma_N_Emn = (1.0 - H * w_N_Emn) * E_N * (eps_N_Emn - eps_N_p_Emn)

        return w_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn, sigma_N_Emn

    #-------------------------------------------------------------------------
    # microplane constitutive law (Tangential CSD)-(Pressure sensitive cumulative damage)
    #-------------------------------------------------------------------------
    def get_tangential_law(self, eps_T_Emna, w_T_Emn, z_T_Emn, alpha_T_Emna,
                           eps_T_pi_Emna, sigma_kk):

        E_T = self.E / (1.0 + self.nu)

        sig_pi_trial = E_T * (eps_T_Emna - eps_T_pi_Emna)
        Z = self.K_T * z_T_Emn
        X = self.gamma_T * alpha_T_Emna
        norm_1 = sqrt(
            einsum('Emna,Emna -> Emn', (sig_pi_trial - X), (sig_pi_trial - X)))

        f = norm_1 - self.tau_pi_bar - \
            Z + self.a * sigma_kk

        plas_1 = f > 1e-6
        elas_1 = f < 1e-6

        delta_lamda = f / \
            (E_T / (1.0 - w_T_Emn) + self.gamma_T + self.K_T) * plas_1

        #         print 'f', f
        #         print 'w_T_Emn', w_T_Emn
        #         print 'delta_lamda', delta_lamda
        #         print '*****'

        norm_2 = 1.0 * elas_1 + sqrt(
            einsum('Emna,Emna -> Emn', (sig_pi_trial - X),
                   (sig_pi_trial - X))) * plas_1

        eps_T_pi_Emna[..., 0] = eps_T_pi_Emna[..., 0] + plas_1 * delta_lamda * \
            ((sig_pi_trial[..., 0] - X[..., 0]) /
             (1.0 - w_T_Emn)) / norm_2
        eps_T_pi_Emna[..., 1] = eps_T_pi_Emna[..., 1] + plas_1 * delta_lamda * \
            ((sig_pi_trial[..., 1] - X[..., 1]) /
             (1.0 - w_T_Emn)) / norm_2
        eps_T_pi_Emna[..., 2] = eps_T_pi_Emna[..., 2] + plas_1 * delta_lamda * \
            ((sig_pi_trial[..., 2] - X[..., 2]) /
             (1.0 - w_T_Emn)) / norm_2

        Y = 0.5 * E_T * \
            einsum('Emna,Emna -> Emn', (eps_T_Emna - eps_T_pi_Emna),
                   (eps_T_Emna - eps_T_pi_Emna))

        w_T_Emn += ((1.0 - w_T_Emn) ** self.c) * \
            (delta_lamda * (Y / self.S) ** self.r)  # * \
        #(self.tau_pi_bar / (self.tau_pi_bar - self.a * sigma_kk / 3.0))

        alpha_T_Emna[..., 0] = alpha_T_Emna[..., 0] + plas_1 * delta_lamda *\
            (sig_pi_trial[..., 0] - X[..., 0]) / norm_2
        alpha_T_Emna[..., 1] = alpha_T_Emna[..., 1] + plas_1 * delta_lamda *\
            (sig_pi_trial[..., 1] - X[..., 1]) / norm_2
        alpha_T_Emna[..., 2] = alpha_T_Emna[..., 2] + plas_1 * delta_lamda *\
            (sig_pi_trial[..., 2] - X[..., 2]) / norm_2
        z_T_Emn = z_T_Emn + delta_lamda

        return w_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna

    #-------------------------------------------------------------------------
    # MICROPLANE-Kinematic constraints
    #-------------------------------------------------------------------------
    def _get_e_Emna(self, eps_Emab):
        # Projection of apparent strain onto the individual microplanes
        e_ni = einsum('nb,Emba->Emna', self._MPN, eps_Emab)
        return e_ni

    def _get_e_N_Emn(self, e_Emna):
        # get the normal strain array for each microplane
        e_N_Emn = einsum('Emna, na->Emn', e_Emna, self._MPN)
        return e_N_Emn

    def _get_e_T_Emna(self, e_Emna):
        # get the tangential strain vector array for each microplane
        e_N_Emn = self._get_e_N_Emn(e_Emna)
        e_N_Emna = einsum('Emn,na->Emna', e_N_Emn, self._MPN)
        return e_Emna - e_N_Emna

    #-------------------------------------------------
    # Alternative methods for the kinematic constraint
    #-------------------------------------------------
    # get the dyadic product of the microplane normals
    _MPNN = Property(depends_on='n_mp')

    @cached_property
    def _get__MPNN(self):
        MPNN_nij = einsum('ni,nj->nij', self._MPN, self._MPN)
        return MPNN_nij

    # get the third order tangential tensor (operator) for each microplane
    _MPTT = Property(depends_on='n_mp')

    @cached_property
    def _get__MPTT(self):
        delta = identity(3)
        MPTT_nijr = 0.5 * (
            einsum('ni,jr -> nijr', self._MPN, delta) +
            einsum('nj,ir -> njir', self._MPN, delta) -
            2 * einsum('ni,nj,nr -> nijr', self._MPN, self._MPN, self._MPN))
        return MPTT_nijr

    def _get_e_N_Emn_2(self, eps_Emab):
        # Projection of apparent strain onto the individual microplanes
        return einsum('nij,Emij->Emn', self._MPNN, eps_Emab)

    def _get_e_T_Emna_2(self, eps_Emab):
        # get the normal strain array for each microplane
        MPTT_ijr = self._get__MPTT()
        return einsum('nija,Emij->Emna', MPTT_ijr, eps_Emab)

    def _get_e_Emna_2(self, eps_Emab):
        # get the tangential strain vector array for each microplane
        return self._get_e_N_Emn_2(eps_Emab) * self._MPN +\
            self._get_e_T_Emna_2(eps_Emab)

    #--------------------------------------------------------
    # return the state variables (Damage , inelastic strains)
    #--------------------------------------------------------
    def _get_state_variables(self, sctx, eps_app_eng, sigma_kk):

        e_N_arr = self._get_e_N_arr_2(eps_app_eng)
        e_T_vct_arr = self._get_e_T_vct_arr_2(eps_app_eng)

        sctx_arr = zeros_like(sctx)

        sctx_N = self.get_normal_law(e_N_arr, sctx)
        sctx_arr[:, 0:5] = sctx_N

        sctx_tangential = self.get_tangential_law(e_T_vct_arr, sctx, sigma_kk)
        sctx_arr[:, 5:13] = sctx_tangential

        return sctx_arr

    #-----------------------------------------------------------------
    # Returns a list of the plastic normal strain  for all microplanes.
    #-----------------------------------------------------------------
    def _get_eps_N_p_Emn(self, eps_Emab, w_N_Emn, z_N_Emn, alpha_N_Emn,
                         r_N_Emn, eps_N_p_Emn):
        eps_N_Emn = self._get_e_N_Emn_2(eps_Emab)
        eps_N_p_Emn = self.get_normal_law(eps_N_Emn, w_N_Emn, z_N_Emn,
                                          alpha_N_Emn, r_N_Emn, eps_N_p_Emn)[4]
        return eps_N_p_Emn

    #----------------------------------------------------------------
    # Returns a list of the sliding strain vector for all microplanes.
    #----------------------------------------------------------------
    def _get_eps_T_pi_Emna(self, eps_Emab, w_T_Emn, z_T_Emn, alpha_T_Emna,
                           eps_T_pi_Emna, sigma_kk_Em):

        eps_T_Emna = self._get_e_T_Emna_2(eps_Emab)
        eps_N_T_pi_Emna = self.get_tangential_law(eps_T_Emna, w_T_Emn, z_T_Emn,
                                                  alpha_T_Emna, eps_T_pi_Emna,
                                                  sigma_kk_Em)[3]

        return eps_N_T_pi_Emna

    #-------------------------------------------------------------
    # Returns a list of the integrity factors for all microplanes.
    #-------------------------------------------------------------
    def _get_phi_Emn(self, eps_Emab, w_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn,
                     eps_N_p_Emn, w_T_Emn, z_T_Emn, alpha_T_Emna,
                     eps_T_pi_Emna, sigma_N_Emn):

        eps_N_Emn = self._get_e_N_Emn_2(eps_Emab)
        eps_T_Emna = self._get_e_T_Emna_2(eps_Emab)

        w_N_Emn = self.get_normal_law(eps_N_Emn, w_N_Emn, z_N_Emn, alpha_N_Emn,
                                      r_N_Emn, eps_N_p_Emn)[0]
        sigma_N_Emn = self.get_normal_law(eps_N_Emn, w_N_Emn, z_N_Emn,
                                          alpha_N_Emn, r_N_Emn, eps_N_p_Emn)[5]
        w_T_Emn = self.get_tangential_law(eps_T_Emna, w_T_Emn, z_T_Emn,
                                          alpha_T_Emna, eps_T_pi_Emna,
                                          sigma_N_Emn)[0]

        w_Emn = zeros_like(w_N_Emn)

        #         w_Emn = np.zeros_like(w_N_Emn)
        #         idx_1 = np.where(sigma_N_Emn >= 0)
        #         w_Emn[idx_1] = w_N_Emn[idx_1]
        #
        #         idx_2 = np.where(sigma_N_Emn < 0)
        #         w_Emn[idx_2] = w_T_Emn[idx_2]
        #
        eig = np.linalg.eig(eps_Emab)[0]

        ter_1 = np.sum(eig)
        idx_1 = np.where(ter_1 > 0.0)
        idx_2 = np.where(ter_1 <= 0.0)

        w_Emn[idx_1] = w_N_Emn[idx_1]
        w_Emn[idx_2] = w_T_Emn[idx_2]

        phi_Emn = 1.0 - w_Emn

        return phi_Emn

    #----------------------------------------------
    # Returns the 2nd order damage tensor 'phi_mtx'
    #----------------------------------------------
    def _get_phi_Emab(self, phi_Emn):

        # integration terms for each microplanes
        phi_Emab = einsum('Emn,n,nab->Emab', phi_Emn, self._MPW, self._MPNN)

        return phi_Emab
#
#     #----------------------------------------------------------------------
#     # Returns the 4th order damage tensor 'beta4' using sum-type symmetrization
#     #(cf. [Jir99], Eq.(21))
#     #----------------------------------------------------------------------
#     def _get_beta_Emabcd(self, phi_Emab):
#
#         delta = identity(3)
#
#         # use numpy functionality (einsum) to evaluate [Jir99], Eq.(21)
#         beta_Emabcd = 0.25 * (einsum('Emik,jl->Emijkl', phi_Emab, delta) +
#                               einsum('Emil,jk->Emijkl', phi_Emab, delta) +
#                               einsum('Emjk,il->Emijkl', phi_Emab, delta) +
#                               einsum('Emjl,ik->Emijkl', phi_Emab, delta))
#
#         return beta_Emabcd
#
# #     #----------------------------------------------------------------------
# #     # Returns the 4th order damage tensor 'beta4' using product-type symmetrization
# #     #(cf. [Baz97], Eq.(87))
# #     #----------------------------------------------------------------------
# #     def _get_beta_tns_product_type(self, sctx, eps_app_eng, sigma_kk):
# #
# #         delta = identity(2)
# #
# #         phi_mtx = self._get_phi_mtx(sctx, eps_app_eng, sigma_kk)
# #
# #         n_dim = 2
# #         phi_eig_value, phi_eig_mtx = eigh(phi_mtx)
# #         phi_eig_value_real = array([pe.real for pe in phi_eig_value])
# #         phi_pdc_mtx = zeros((n_dim, n_dim), dtype=float)
# #         for i in range(n_dim):
# #             phi_pdc_mtx[i, i] = phi_eig_value_real[i]
# #         # w_mtx = tensorial square root of the second order damage tensor:
# #         w_pdc_mtx = sqrt(phi_pdc_mtx)
# #
# #         # transform the matrix w back to x-y-coordinates:
# #         w_mtx = einsum('ik,kl,lj -> ij', phi_eig_mtx, w_pdc_mtx, phi_eig_mtx)
# #         #w_mtx = dot(dot(phi_eig_mtx, w_pdc_mtx), transpose(phi_eig_mtx))
# #
# #         beta_ijkl = 0.5 * \
# #             (einsum('ik,jl -> ijkl', w_mtx, w_mtx) +
# #              einsum('il,jk -> ijkl', w_mtx, w_mtx))
# #
# #         return beta_ijkl
#
#     #-----------------------------------------------------------
#     # Integration of the (inelastic) strains for each microplane
#     #-----------------------------------------------------------
#     def _get_eps_p_Emab(self, eps_Emab, w_N_Emn, z_N_Emn,
#                         alpha_N_Emn, r_N_Emn, eps_N_p_Emn,
#                         w_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna,  sigma_N_Emn):
#
#         eps_N_Emn = self._get_e_N_Emn_2(eps_Emab)
#         eps_T_Emna = self._get_e_T_Emna_2(eps_Emab)
#
# #         # plastic normal strains
# #         eps_N_p_Emn = self.get_normal_law(
# # eps_N_Emn, w_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn)[5]
#
#         eps_N_p_Emn = self._get_eps_N_p_Emn(
#             eps_Emab, w_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn)
#
# #         # sliding tangential strains
# #         eps_T_pi_Emna = self.get_tangential_law(
# # eps_T_Emna, w_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna,
# # sigma_N_Emn)[4]
#
#         eps_T_pi_Emna = self._get_eps_T_pi_Emna(
#             eps_Emab, w_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_N_Emn)
#
#         delta = identity(3)
#
#         # 2-nd order plastic (inelastic) tensor
#         eps_p_Emab = einsum('n,Emn,na,nb -> Emab', self._MPW, eps_N_p_Emn, self._MPN, self._MPN) + \
#             0.5 * (einsum('n,Emnf,na,fb-> Emab', self._MPW, eps_T_pi_Emna, self._MPN, delta) +
#                    einsum('n,Emnf,nb,fa-> Emab', self._MPW, eps_T_pi_Emna, self._MPN, delta))
#
#         return eps_p_Emab
#
#     #-------------------------------------------------------------------------
#     # Evaluation - get the corrector and predictor
#     #-------------------------------------------------------------------------
#
#     def get_corr_pred(self, eps_Emab_n1, deps_Emab, tn, tn1, update_state, w_N_Emn, z_N_Emn,
#                       alpha_N_Emn, r_N_Emn, eps_N_p_Emn, sigma_N_Emn,
#                       w_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna):
#
#         # Corrector predictor computation.
#         if update_state:
#
#             eps_Emab_n = eps_Emab_n1 - deps_Emab
#
#             eps_N_Emn = self._get_e_N_Emn_2(eps_Emab_n)
#             eps_T_Emna = self._get_e_T_Emna_2(eps_Emab_n)
#
#             w_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn, sigma_N_Emn = self.get_normal_law(
#                 eps_N_Emn, w_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn)
#             w_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna = self.get_tangential_law(
#                 eps_T_Emna, w_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_N_Emn)
#
#         #------------------------------------------------------------------
#         # Damage tensor (4th order) using product- or sum-type symmetrization:
#         #------------------------------------------------------------------
#         phi_Emn = self._get_phi_Emn(eps_Emab_n1, w_N_Emn, z_N_Emn,
#                                     alpha_N_Emn, r_N_Emn, eps_N_p_Emn,
#                                     w_T_Emn, z_T_Emn, alpha_T_Emna,
#                                     eps_T_pi_Emna, sigma_N_Emn)
#
#         phi_Emab = self._get_phi_Emab(phi_Emn)
#
#         beta_Emabcd = self._get_beta_Emabcd(phi_Emab)
#
#         #------------------------------------------------------------------
#         # Damaged stiffness tensor calculated based on the damage tensor beta4:
#         #------------------------------------------------------------------
#         D_Emabcd = einsum(
#             'Emijab, abef, Emcdef -> Emijcd', beta_Emabcd, self.D_abef, beta_Emabcd)
#         #----------------------------------------------------------------------
#         # Return stresses (corrector) and damaged secant stiffness matrix (predictor)
#         #----------------------------------------------------------------------
#         # plastic strain tensor
#         eps_p_Emab = self._get_eps_p_Emab(eps_Emab_n1, w_N_Emn, z_N_Emn,
#                                           alpha_N_Emn, r_N_Emn, eps_N_p_Emn,
#                                           w_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_N_Emn)
#
#         # elastic strain tensor
#         eps_e_Emab = eps_Emab_n1 - eps_p_Emab
#
#         # calculation of the stress tensor
#         sig_Emab = einsum('Emabcd,Emcd -> Emab', D_Emabcd, eps_e_Emab)
#
#         return D_Emabcd, sig_Emab

#----------------------------------------------------------------
#  the fourth order volumetric-identity tensor
#----------------------------------------------------------------

    def _get_I_vol_abcd(self):

        delta = identity(3)
        I_vol_abcd = (1.0 / 3.0) * einsum('ab,cd -> abcd', delta, delta)
        return I_vol_abcd

    #----------------------------------------------------------------
    # Returns the fourth order deviatoric-identity tensor
    #----------------------------------------------------------------
    def _get_I_dev_abcd(self):

        delta = identity(3)
        I_dev_abcd = 0.5 * (einsum('ac,bd -> abcd', delta, delta) +
                            einsum('ad,bc -> abcd', delta, delta)) \
            - (1.0 / 3.0) * einsum('ab,cd -> abcd', delta, delta)

        return I_dev_abcd

    #----------------------------------------------------------------
    # Returns the fourth order tensor P_vol [Wu.2009]
    #----------------------------------------------------------------
    def _get_P_vol_ab(self):

        delta = identity(3)
        P_vol_ab = (1.0 / 3.0) * delta
        return P_vol_ab

    #----------------------------------------------------------------
    # Returns the fourth order tensor P_dev [Wu.2009]
    #----------------------------------------------------------------
    def _get_P_dev_nabc(self):

        delta = identity(3)
        P_dev_nabc = 0.5 * einsum('nd,da,bc -> nabc', self._MPN, delta, delta)
        return P_dev_nabc

    #----------------------------------------------------------------
    # Returns the outer product of P_vol [Wu.2009]
    #----------------------------------------------------------------
    def _get_PP_vol_abcd(self):

        delta = identity(3)
        PP_vol_abcd = (1.0 / 9.0) * einsum('ab,cd -> abcd', delta, delta)
        return PP_vol_abcd

    #----------------------------------------------------------------
    # Returns the inner product of P_dev
    #----------------------------------------------------------------
    def _get_PP_dev_nabcd(self):

        delta = identity(3)
        PP_dev_nabcd = 0.5 * (0.5 * (einsum('na,nc,bd -> nabcd', self._MPN, self._MPN, delta) +
                                     einsum('na,nd,bc -> nabcd', self._MPN, self._MPN, delta)) +
                              0.5 * (einsum('ac,nb,nd -> nabcd',  delta, self._MPN, self._MPN) +
                                     einsum('ad,nb,nc -> nabcd',  delta, self._MPN, self._MPN))) -\
            (1.0 / 3.0) * (einsum('na,nb,cd -> nabcd', self._MPN, self._MPN, delta) +
                           einsum('ab,nc,nd -> nabcd', delta, self._MPN, self._MPN)) +\
            (1.0 / 9.0) * einsum('ab,cd -> abcd', delta, delta)

        return PP_dev_nabcd

    def _get_I_vol_4(self):
        # The fourth order volumetric-identity tensor
        delta = identity(3)
        I_vol_ijkl = (1.0 / 3.0) * einsum('ij,kl -> ijkl', delta, delta)
        return I_vol_ijkl

    def _get_I_dev_4(self):
        # The fourth order deviatoric-identity tensor
        delta = identity(3)
        I_dev_ijkl = 0.5 * (einsum('ik,jl -> ijkl', delta, delta) +
                            einsum('il,jk -> ijkl', delta, delta)) \
            - (1 / 3.0) * einsum('ij,kl -> ijkl', delta, delta)

        return I_dev_ijkl

    def _get_P_vol(self):
        delta = identity(3)
        P_vol_ij = (1 / 3.0) * delta
        return P_vol_ij

    def _get_P_dev(self):
        delta = identity(3)
        P_dev_njkl = 0.5 * einsum('ni,ij,kl -> njkl', self._MPN, delta, delta)
        return P_dev_njkl

    def _get_PP_vol_4(self):
        # outer product of P_vol
        delta = identity(3)
        PP_vol_ijkl = (1 / 9.) * einsum('ij,kl -> ijkl', delta, delta)
        return PP_vol_ijkl

    def _get_PP_dev_4(self):
        # inner product of P_dev
        delta = identity(3)
        PP_dev_nijkl = 0.5 * (0.5 * (einsum('ni,nk,jl -> nijkl', self._MPN, self._MPN, delta) +
                                     einsum('ni,nl,jk -> nijkl', self._MPN, self._MPN, delta)) +
                              0.5 * (einsum('ik,nj,nl -> nijkl',  delta, self._MPN, self._MPN) +
                                     einsum('il,nj,nk -> nijkl',  delta, self._MPN, self._MPN))) -\
            (1 / 3.) * (einsum('ni,nj,kl -> nijkl', self._MPN, self._MPN, delta) +
                        einsum('ij,nk,nl -> nijkl', delta, self._MPN, self._MPN)) +\
            (1 / 9.) * einsum('ij,kl -> ijkl', delta, delta)
        return PP_dev_nijkl

    #--------------------------------------------------------------------------
    # Returns the fourth order secant stiffness tensor (cf. [Wu.2009], Eq.(29))
    #--------------------------------------------------------------------------
    def _get_S_1_Emabcd(self, eps_Emab, phi_Emn):

        K0 = self.E / (1.0 - 2.0 * self.nu)
        G0 = self.E / (1.0 + self.nu)

        #phi_Emn = 1.0 - self._get_omega(kappa)
        # print 'phi_Emn', phi_Emn

        PP_vol_abcd = self._get_PP_vol_abcd()
        PP_dev_nabcd = self._get_PP_dev_nabcd()
        I_dev_abcd = self._get_I_dev_abcd()

        #         PP_vol_abcd = self._get_PP_vol_4()
        #         PP_dev_nabcd = self._get_PP_dev_4()
        #         I_dev_abcd = self._get_I_dev_4()

        S_1_Emabcd = K0 * einsum('Emn, n, abcd-> Emabcd', phi_Emn, self._MPW, PP_vol_abcd) + \
            G0 * 2.0 * self.zeta_G * einsum('Emn, n, nabcd-> Emabcd',
                                            phi_Emn, self._MPW, PP_dev_nabcd) - (1.0 / 3.0) * (
                2.0 * self.zeta_G - 1.0) * G0 * einsum('Emn, n, abcd-> Emabcd',
                                                       phi_Emn, self._MPW, I_dev_abcd)

        return S_1_Emabcd

#     #------------------------------------------
#     # scalar damage factor for each microplane
#     #------------------------------------------
#     def _get_d_Em(self, s_Emng, eps_Emab):
#
#         d_Emn = 1.0 - self.get_state_variables(s_Emng, eps_Emab)[0]
#
#         d_Em = (1.0 / 3.0) * einsum('Emn,n-> Em',  d_Emn, self._MPW)
#
#         return d_Em
#
#     #------------------------------------------
#     # The 4th order volumetric damage tensor
#     #------------------------------------------
#     def _get_M_vol_abcd(self, sctx, eps_app_eng, sigma_kk):
#
#         d = self._get_Em( s_Emng, eps_Emab)
#         delta = identity(2)
#
#         I_4th_abcd = 0.5 * (einsum('ac,bd -> ijkl', delta, delta) +
#                             einsum('il,jk -> ijkl', delta, delta))
#
#         # print 'M_vol', (1 - d) * I_4th_ijkl
#
#         return (1 - d) * I_4th_ijkl
#
#     #------------------------------------------
#     # The 4th order deviatoric damage tensor
#     #------------------------------------------
#     def _get_M_dev_tns(self, phi_mtx):
#
#         delta = identity(3)
#         I_4th_ijkl = 0.5 * (einsum('ik,jl -> ijkl', delta, delta) +
#                             einsum('il,jk -> ijkl', delta, delta))
#         tr_phi_mtx = np.trace(phi_mtx)
#
#         M_dev_ijkl = self.zeta_G * (0.5 * (einsum('ik,jl->ijkl', delta, phi_mtx) +
#                                            einsum('il,jk->ijkl', delta, phi_mtx)) +
#                                     0.5 * (einsum('ik,jl->ijkl', phi_mtx, delta) +
#                                            einsum('il,jk->ijkl', phi_mtx, delta))) \
#             - (2. * self.zeta_G - 1.) * (tr_phi_mtx / 3.) * I_4th_ijkl
#
#         return M_dev_ijkl
#
#     #--------------------------------------------------------------------------
#     # Returns the fourth order secant stiffness tensor (cf. [Wu.2009], Eq.(31))
#     #--------------------------------------------------------------------------
#     def _get_S_2_Emabcd(self, sctx, eps_app_eng, sigma_kk):
#
#         K0 = self.E / (1. - 2. * self.nu)
#         G0 = self.E / (1. + self.nu)
#
#         I_vol_ijkl = self._get_I_vol_4()
#         I_dev_ijkl = self._get_I_dev_4()
#         phi_mtx = self._get_phi_mtx(sctx, eps_app_eng, sigma_kk)
#         M_vol_ijkl = self._get_M_vol_tns(sctx, eps_app_eng, sigma_kk)
#         M_dev_ijkl = self._get_M_dev_tns(phi_mtx)
#
#         S_2_ijkl = K0 * einsum('ijmn,mnrs,rskl -> ijkl', I_vol_ijkl, M_vol_ijkl, I_vol_ijkl ) \
#             + G0 * einsum('ijmn,mnrs,rskl -> ijkl', I_dev_ijkl, M_dev_ijkl, I_dev_ijkl)\
#
#         return S_2_ijkl
#
#     #--------------------------------------------------------------------------
#     # Returns the fourth order secant stiffness tensor (cf. [Wu.2009], Eq.(34))
#     #--------------------------------------------------------------------------
#     def _get_S_3_Emabcd(self, sctx, eps_app_eng, sigma_kk):
#
#         K0 = self.E / (1. - 2. * self.nu)
#         G0 = self.E / (1. + self.nu)
#
#         I_vol_ijkl = self._get_I_vol_4()
#         I_dev_ijkl = self._get_I_dev_4()
#
#         # The fourth order elastic stiffness tensor
#         S_0_ijkl = K0 * I_vol_ijkl + G0 * I_dev_ijkl
#
#         d_n = self._get_state_variables(sctx, eps_app_eng, sigma_kk)[:, 5]
#
#         PP_vol_4 = self._get_PP_vol_4()
#         PP_dev_4 = self._get_PP_dev_4()
#
#         delta = identity(3)
#         I_4th_ijkl = einsum('ik,jl -> ijkl', delta, delta)
#
#         D_ijkl = einsum('n,n,ijkl->ijkl', d_n, self._MPW, PP_vol_4) + \
#             2 * self.zeta_G * einsum('n,n,nijkl->ijkl', d_n, self._MPW, PP_dev_4) - (
#                 1 / 3.) * (2 * self.zeta_G - 1) * einsum('n,n,ijkl->ijkl', d_n, self._MPW, I_dev_ijkl)
#
#         phi_ijkl = (I_4th_ijkl - D_ijkl)
#
#         S_ijkl = einsum('ijmn,mnkl', phi_ijkl, S_0_ijkl)
#
#         return S_ijkl
#
#-------------------------------------------------------------------------
# Returns the fourth order secant stiffness tensor with the (double orthotropic) assumption
#-------------------------------------------------------------------------

    def _get_S_4_Emabcd(self, eps_Emab, phi_Emab):

        K0 = self.E / (1.0 - 2.0 * self.nu)
        G0 = self.E / (1.0 + self.nu)
        I_vol_abcd = self._get_I_vol_abcd()
        I_dev_abcd = self._get_I_dev_abcd()

        delta = identity(3)
        #phi_Emab = self._get_phi_Emab(kappa)

        D_Emab = delta - phi_Emab

        d_Em = (1.0 / 3.0) * np.einsum('Emaa -> Em', D_Emab)

        D_bar_Emab = self.zeta_G * \
            (D_Emab - np.einsum('Em, ab -> Emab', d_Em, delta))

        S_4_Emabcd = K0 * I_vol_abcd - K0 * einsum('Em,abcd -> Emabcd', d_Em, I_vol_abcd) +\
            G0 * I_dev_abcd - G0 * einsum('Em,abcd -> Emabcd', d_Em, I_dev_abcd) +\
            (2.0 / 3.0) * (G0 - K0) * (einsum('ij,Emkl -> Emijkl', delta, D_bar_Emab) +
                                       einsum('Emij,kl -> Emijkl', D_bar_Emab, delta)) + 0.5 * (- K0 + 2.0 * G0) *\
            (0.5 * (einsum('ik,Emjl -> Emijkl', delta, D_bar_Emab) + einsum('Emil,jk -> Emijkl', D_bar_Emab, delta)) +
             0.5 * (einsum('Emil,jk -> Emijkl', D_bar_Emab, delta) + einsum('ik,Emjl -> Emijkl', delta, D_bar_Emab)))

        return S_4_Emabcd

    #----------------------------------------------------------------------
    # Returns the fourth order secant stiffness tensor (restrctive orthotropic)
    #----------------------------------------------------------------------
    def _get_S_5_Emabcd(self, eps_Emab, phi_Emab):

        K0 = self.E / (1.0 - 2.0 * self.nu)
        G0 = self.E / (1.0 + self.nu)

        delta = identity(3)
        # commented out - @rch, @abdul please check
        # phi_Emab = self._get_phi_Emab(kappa)

        # damaged stiffness without simplification
        S_5_Emabcd = (1.0 / 3.0) * (K0 - G0) * 0.5 * ((einsum('ij,Emkl -> Emijkl', delta, phi_Emab) +
                                                       einsum('Emij,kl -> Emijkl', phi_Emab, delta))) + \
            G0 * 0.5 * ((0.5 * (einsum('ik,Emjl -> Emijkl', delta, phi_Emab) + einsum('Emil,jk -> Emijkl', phi_Emab, delta)) +
                         0.5 * (einsum('Emik,jl -> ijkl', phi_Emab, delta) + einsum('il,Emjk  -> Emijkl', delta, phi_Emab))))

        return S_5_Emabcd

    #-------------------------------------------------------------------------
    # Evaluation - get the corrector and predictor
    #-------------------------------------------------------------------------

    def get_corr_pred(self, eps_Emab_n1, deps_Emab, tn, tn1, update_state,
                      w_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn,
                      sigma_N_Emn, w_T_Emn, z_T_Emn, alpha_T_Emna,
                      eps_T_pi_Emna):

        if update_state:

            eps_Emab_n = eps_Emab_n1 - deps_Emab

            eps_N_Emn = self._get_e_N_Emn_2(eps_Emab_n)
            eps_T_Emna = self._get_e_T_Emna_2(eps_Emab_n)

            w_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn, sigma_N_Emn = self.get_normal_law(
                eps_N_Emn, w_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn)
            w_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna = self.get_tangential_law(
                eps_T_Emna, w_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna,
                sigma_N_Emn)

        phi_Emn = self._get_phi_Emn(eps_Emab_n1, w_N_Emn, z_N_Emn, alpha_N_Emn,
                                    r_N_Emn, eps_N_p_Emn, w_T_Emn, z_T_Emn,
                                    alpha_T_Emna, eps_T_pi_Emna, sigma_N_Emn)

        phi_Emab = self._get_phi_Emab(phi_Emn)

        D_Emabcd = self._get_S_4_Emabcd(eps_Emab_n1, phi_Emab)

        sig_Emab = einsum('Emabcd,Emcd -> Emab', D_Emabcd, eps_Emab_n1)

        return D_Emabcd, sig_Emab

    #-----------------------------------------------
    # number of microplanes - currently fixed for 3D
    #-----------------------------------------------
    n_mp = Constant(28)

    #-----------------------------------------------
    # get the normal vectors of the microplanes
    #-----------------------------------------------
    _MPN = Property(depends_on='n_mp')

    @cached_property
    def _get__MPN(self):
        return array([[.577350259, .577350259, .577350259],
                      [.577350259, .577350259, -.577350259],
                      [.577350259, -.577350259, .577350259],
                      [.577350259, -.577350259, -.577350259],
                      [.935113132, .250562787, .250562787],
                      [.935113132, .250562787, -.250562787],
                      [.935113132, -.250562787, .250562787],
                      [.935113132, -.250562787, -.250562787],
                      [.250562787, .935113132, .250562787],
                      [.250562787, .935113132, -.250562787],
                      [.250562787, -.935113132, .250562787],
                      [.250562787, -.935113132, -.250562787],
                      [.250562787, .250562787, .935113132],
                      [.250562787, .250562787, -.935113132],
                      [.250562787, -.250562787, .935113132],
                      [.250562787, -.250562787, -.935113132],
                      [.186156720, .694746614, .694746614],
                      [.186156720, .694746614, -.694746614],
                      [.186156720, -.694746614, .694746614],
                      [.186156720, -.694746614, -.694746614],
                      [.694746614, .186156720, .694746614],
                      [.694746614, .186156720, -.694746614],
                      [.694746614, -.186156720, .694746614],
                      [.694746614, -.186156720, -.694746614],
                      [.694746614, .694746614, .186156720],
                      [.694746614, .694746614, -.186156720],
                      [.694746614, -.694746614, .186156720],
                      [.694746614, -.694746614, -.186156720]])

    #-------------------------------------
    # get the weights of the microplanes
    #-------------------------------------
    _MPW = Property(depends_on='n_mp')

    @cached_property
    def _get__MPW(self):
        return array([
            .0160714276, .0160714276, .0160714276, .0160714276, .0204744730,
            .0204744730, .0204744730, .0204744730, .0204744730, .0204744730,
            .0204744730, .0204744730, .0204744730, .0204744730, .0204744730,
            .0204744730, .0158350505, .0158350505, .0158350505, .0158350505,
            .0158350505, .0158350505, .0158350505, .0158350505, .0158350505,
            .0158350505, .0158350505, .0158350505
        ]) * 6.0
Beispiel #25
0
class FETS2DMITC(FETSEval):
    r'''MITC3 shell finite element:
        See https://www.sesamx.io/blog/standard_linear_triangular_shell_element/
        See http://dx.doi.org/10.1016/j.compstruc.2014.02.005
    '''

    # vtk_r = tr.Array(np.float_, value=[[1, 0, 0], [0, 1, 0], [0, 0, 1]])
    # vtk_cells = [[0, 1, 2]]
    # vtk_cell_types = 'Triangle'
    # vtk_cell = [0, 1, 2]
    # vtk_cell_type = 'Triangle'
    # vtk_expand_operator = tr.Array(np.float_, value=DELTA23_ab)

    # =========================================================================
    # Surface integrals using numerical integration
    # =========================================================================
    # j is gauss point index, p is point coords in natural coords (zeta_1, zeta_2, zeta_3)
    eta_jp = tr.Array('float_')
    r'''Integration points within a triangle.
    '''
    def _eta_jp_default(self):
        # We applay the 7-point Gauss integration to integrate exactly on the plane defined by rr and ss
        # [r, s, t] at each Gauss point (r, s, t) are natural coords in the shell element r,s in plane
        # and t along thickness
        # 2 Gauss points along thickness t
        # 7 Gauss points on the plane of the element
        return np.array(
            [[1. / 3., 1. / 3., 1. / 3.]], dtype='f'
        )  # should be return np.array([[1. / 3., 1. / 3., 0]], dtype='f')

    w_m = tr.Array('float_')
    r'''Weight factors for numerical integration.'''

    def _w_m_default(self):
        print('w_m called!!')
        return np.array([1], dtype='f')

    # TODO, different node thickness in each node according to the original
    #  implementation can be easily integrated, but here the same thickness
    #  is used for simplicity.
    a = tr.Float(1.0, label='thickness')

    n_m = tr.Property(depends_on='w_m')
    r'''Number of integration points.'''

    @tr.cached_property
    def _get_n_m(self):
        return len(self.w_m)

    n_nodal_dofs = tr.Int(5)

    dh_imr = tr.Property(depends_on='eta_jp')
    r'''Derivatives of the shape functions in the integration points.'''

    @tr.cached_property
    def _get_dh_imr(self):
        # Same for all Gauss points
        dh_ri = np.array(
            [
                [1, 0, -1],  # dh1/d_r, dh2/d_r, dh3/d_r
                [0, 1, -1],  # dh1/d_s, dh2/d_s, dh3/d_s
                [0, 0, 0]
            ],  # dh1/d_t, dh2/d_t, dh3/d_t
            dtype=np.float_)

        dh_mri = np.tile(dh_ri, (self.n_m, 1, 1))

        # dh_mri = np.flip(dh_mri, 2)
        print('dh_imr called!')
        return np.einsum('mri->imr', dh_mri)

    dht_imr = tr.Property(depends_on='eta_jp')
    r'''Derivatives of the (shape functions * t) in the integration points.
    '''

    @tr.cached_property
    def _get_dht_imr(self):
        # m: gauss points, r: r, s, t, and i: h1, h2, h3
        eta_jp = self.eta_jp
        dht_mri = np.array(
            [
                [
                    [t, 0, -t],  # (t*dh1)/d_r, (t*dh2)/d_r, (t*dh3)/d_r
                    [0, t, -t],  # (t*dh1)/d_s, (t*dh2)/d_s, (t*dh3)/d_s
                    [r, s, 1 - r - s]
                ]  # (t*dh1)/d_t, (t*dh2)/d_t, (t*dh3)/d_t
                for r, s, t in zip(eta_jp[:, 0], eta_jp[:, 1], eta_jp[:, 2])
            ],
            dtype=np.float_)

        # dht_mri = np.flip(dht_mri, 2)
        return np.einsum('mri->imr', dht_mri)
class SlideExplorer(bu.Model):
    name = 'Explorer'

    tree = [
        'slide_model', 'inel_state_evolution', 'energy_dissipation', 'tf_s_x',
        'tf_s_y', 'tf_w'
    ]

    slide_model = bu.Instance(Slide32, (), tree=True)
    #    slide_model = bu.Instance(Slide34, (), tree=True)

    energy_dissipation = bu.Instance(EnergyDissipation, tree=True)
    '''Viewer to the energy dissipation'''
    def _energy_dissipation_default(self):
        return EnergyDissipation(slider_exp=self)

    inel_state_evolution = bu.Instance(InelStateEvolution, tree=True)
    '''Viewer to the inelastic state evolution'''

    def _inel_state_evolution_default(self):
        return InelStateEvolution(slider_exp=self)

    time_fn = bu.Instance(TimeFunction, (), tree=True)

    def __init__(self, *args, **kw):
        super(SlideExplorer, self).__init__(*args, **kw)
        self.reset_i()

    n_Eps = tr.Property()

    def _get_n_Eps(self):
        return len(self.slide_model.symb.Eps)

    s_x_1 = bu.Float(0, INC=True)
    s_y_1 = bu.Float(0, INC=True)
    w_1 = bu.Float(0, INC=True)

    tf_s_x = bu.Instance(TimeFunction, TIME=True)

    def _tf_s_x_default(self):
        return TFSelector()

    tf_s_y = bu.Instance(TimeFunction, TIME=True)

    def _tf_s_y_default(self):
        return TFSelector()

    tf_w = bu.Instance(TimeFunction, TIME=True)

    def _tf_w_default(self):
        return TFSelector()

    n_steps = bu.Int(10, ALG=True)
    k_max = bu.Int(20, ALG=True)

    Sig_arr = tr.Array
    Eps_arr = tr.Array

    Sig_t = tr.Property

    def _get_Sig_t(self):
        return self.Sig_arr

    Eps_t = tr.Property

    def _get_Eps_t(self):
        return self.Eps_arr

    ipw_view = bu.View(bu.Item('s_x_1', latex=r's_x'),
                       bu.Item('s_y_1', latex=r's_y'),
                       bu.Item('w_1', latex=r'w'),
                       bu.Item('n_steps'),
                       bu.Item('k_max'),
                       bu.Item('t_max', readonly=True),
                       time_editor=bu.ProgressEditor(
                           run_method='run',
                           reset_method='reset',
                           interrupt_var='sim_stop',
                           time_var='t',
                           time_max='t_max',
                       ))

    def reset_i(self):
        self.s_x_0, self.s_y_0, self.w_0 = 0, 0, 0
        self.t0 = 0
        self.t = 0
        self.t_max = 1
        self.Sig_arr = np.zeros((0, self.n_Eps))
        self.Eps_arr = np.zeros((0, self.n_Eps))
        self.Sig_record = []
        self.Eps_record = []
        self.iter_record = []
        self.t_arr = []
        self.s_x_t, self.s_y_t, self.w_t = [], [], []
        self.Eps_n1 = np.zeros((self.n_Eps, ), dtype=np.float_)
        self.Sig_n1 = np.zeros((self.n_Eps, ), dtype=np.float_)
        self.s_x_1 = 0
        self.s_y_1 = 0
        self.w_1 = 0

    t = bu.Float(0)
    t_max = bu.Float(1)

    def _t_max_changed(self):
        self.inel_state_evolution.t_max = self.t_max

    def get_response_i(self, update_progress=lambda t: t):
        # global Eps_record, Sig_record, iter_record
        # global t_arr, s_x_t, s_y_t, w_t, s_x_0, s_y_0, w_0, t0, Eps_n1
        n_steps = self.n_steps
        t_i = np.linspace(0, 1, n_steps + 1)
        t1 = self.t0 + 1
        self.t_max = t1
        ti_arr = np.linspace(self.t0, t1, n_steps + 1)
        delta_t = t1 - self.t0
        tf_s_x = self.tf_s_x(np.linspace(0, delta_t, n_steps + 1))
        tf_s_y = self.tf_s_y(np.linspace(0, delta_t, n_steps + 1))
        tf_w = self.tf_w(np.linspace(0, delta_t, n_steps + 1))
        # si_x_t = tf_s_x * np.linspace(self.s_x_0, self.s_x_1, n_steps + 1) + 1e-9
        # si_y_t = tf_s_y * np.linspace(self.s_y_0, self.s_y_1, n_steps + 1) + 1e-9
        # wi_t = tf_w * np.linspace(self.w_0, self.w_1, n_steps + 1) + 1e-9
        si_x_t = self.s_x_0 + tf_s_x * (self.s_x_1 - self.s_x_0) + 1e-9
        si_y_t = self.s_y_0 + tf_s_y * (self.s_y_1 - self.s_y_0) + 1e-9
        wi_t = self.w_0 + tf_w * (self.w_1 - self.w_0) + 1e-9
        for i, (t, s_x_n1, s_y_n1,
                w_n1) in enumerate(zip(t_i, si_x_t, si_y_t, wi_t)):
            if self.slide_model.debug_level == 1:
                print('============= INCREMENT', i)

            try:
                self.Eps_n1, self.Sig_n1, k = self.slide_model.get_sig_n1(
                    s_x_n1, s_y_n1, w_n1, self.Sig_n1, self.Eps_n1, self.k_max)
            except ConvergenceError as e:
                print(e)
                break
            self.Sig_record.append(self.Sig_n1)
            self.Eps_record.append(self.Eps_n1)
            self.iter_record.append(k)
            self.t = t

        self.Sig_arr = np.array(self.Sig_record, dtype=np.float_)
        self.Eps_arr = np.array(self.Eps_record, dtype=np.float_)
        self.iter_t = np.array(self.iter_record, dtype=np.int_)
        n_i = len(self.iter_t)
        self.t_arr = np.hstack([self.t_arr, ti_arr])[:n_i]
        self.s_x_t = np.hstack([self.s_x_t, si_x_t])[:n_i]
        self.s_y_t = np.hstack([self.s_y_t, si_y_t])[:n_i]
        self.w_t = np.hstack([self.w_t, wi_t])[:n_i]
        self.t0 = t1
        self.s_x_0, self.s_y_0, self.w_0 = self.s_x_1, self.s_y_1, self.w_1
        # set the last step index in the response browser
        self.inel_state_evolution.t_max = self.t_arr[-1]
        return

    # ## Plotting functions
    # To simplify postprocessing examples, here are two aggregate plotting functions, one for the state and force variables, the other one for the evaluation of energies

    def plot_sig_w(self, ax):
        sig_t = self.Sig_arr.T[2, ...]
        ax.plot(self.w_t, sig_t, color='orange', lw=3)

    def plot3d_Sig_Eps(self, ax3d):
        tau_x, tau_y = self.Sig_arr.T[:2, ...]
        tau = np.sqrt(tau_x**2 + tau_y**2)
        ax3d.plot3D(self.s_x_t, self.s_y_t, tau, color='orange', lw=2)

    def run(self, update_progress=lambda t: t):
        try:
            self.get_response_i(update_progress)
        except ValueError:
            print('No convergence reached')
            return

    def reset(self):
        self.reset_i()

    def subplots(self, fig):
        ax = fig.add_gridspec(1, 3)
        ax1 = fig.add_subplot(ax[0, 0:2], projection='3d')
        ax2 = fig.add_subplot(ax[0:, -1])
        return ax1, ax2

    def update_plot(self, axes):
        ax_sxy, ax_sig = axes
        self.plot_sig_w(ax_sig)
        ax_sig.set_xlabel(r'$w$ [mm]')
        ax_sig.set_ylabel(r'$\sigma$ [MPa]')

        #    plot_tau_s(ax1, Eps_arr[-1,...],s_max,500,get_g3,**kw)
        ax_sxy.plot(self.s_x_t, self.s_y_t, 0, color='red', lw=1)
        self.plot3d_Sig_Eps(ax_sxy)
        ax_sxy.set_xlabel(r'$s_x$ [mm]')
        ax_sxy.set_ylabel(r'$s_y$ [mm]')
        ax_sxy.set_zlabel(r'$\| \tau \| = \sqrt{\tau_x^2 + \tau_y^2}$ [MPa]')
class Slide34(MATSEval, bu.InjectSymbExpr):

    name = 'Slide 3.4'
    symb_class = Slide34Expr

    E_T = bu.Float(28000, MAT=True)
    gamma_T = bu.Float(10, MAT=True)
    K_T = bu.Float(8, MAT=True)
    S_T = bu.Float(1, MAT=True)
    c_T = bu.Float(1, MAT=True)
    bartau = bu.Float(28000, MAT=True)
    E_N = bu.Float(28000, MAT=True)
    S_N = bu.Float(1, MAT=True)
    c_N = bu.Float(1, MAT=True)
    m = bu.Float(0.1, MAT=True)
    f_t = bu.Float(3, MAT=True)
    f_c = bu.Float(30, MAT=True)
    f_c0 = bu.Float(20, MAT=True)
    eta = bu.Float(0.5, MAT=True)
    r = bu.Float(1, MAT=True)

    c_NT = tr.Property(bu.Float, depends_on='state_changed')

    @tr.cached_property
    def _get_c_NT(self):
        return np.sqrt(self.c_N * self.c_T)

    S_NT = tr.Property(bu.Float, depends_on='state_changed')

    @tr.cached_property
    def _get_S_NT(self):
        return np.sqrt(self.S_N * self.S_T)

    debug = bu.Bool(False)

    def C_codegen(self):

        import os
        import os.path as osp

        C_code = []
        for symb_name, symb_params in self.symb.symb_expressions:
            c_func_name = 'get_' + symb_name
            c_func = ccode(c_func_name, getattr(self.symb, symb_name),
                           'SLIDE33')
            C_code.append(c_func)

        code_dirname = 'sympy_codegen'
        code_fname = 'SLIDE33_3D'

        home_dir = osp.expanduser('~')
        code_dir = osp.join(home_dir, code_dirname)
        if not osp.exists(code_dir):
            os.makedirs(code_dir)

        code_file = osp.join(code_dir, code_fname)

        print('generated code_file', code_file)
        h_file = code_file + '.h'
        c_file = code_file + '.c'

        h_f = open(h_file, 'w')
        c_f = open(c_file, 'w')

        if True:
            for function_C in C_code:

                h_f.write(function_C[1][1])
                c_f.write(function_C[0][1])
        h_f.close()
        c_f.close()

    ipw_view = bu.View(
        bu.Item('E_T', latex='E_T'),
        bu.Item('S_T'),
        bu.Item('c_T'),
        bu.Item('gamma_T'),
        bu.Item('K_T'),
        bu.Item('bartau', latex=r'\bar{\tau}'),
        bu.Item('E_N'),
        bu.Item('S_N'),
        bu.Item('c_N'),
        bu.Item('m'),
        bu.Item('f_t'),
        bu.Item('f_c', latex=r'f_\mathrm{c}'),
        bu.Item('f_c0', latex=r'f_\mathrm{c0}'),
        bu.Item('eta'),
        bu.Item('r'),
        bu.Item('c_NT', readonly=True),
        bu.Item('S_NT', readonly=True),
    )

    damage_interaction = tr.Enum('final', 'geometric', 'arithmetic')

    get_phi_ = tr.Property

    def _get_get_phi_(self):
        return self.symb.get_phi_final_

    get_Phi_ = tr.Property

    def _get_get_Phi_(self):
        return self.symb.get_Phi_final_

    def get_f_df(self, u_N_n1, u_Tx_n1, u_Ty_n1, Sig_k, Eps_k):
        if self.debug:
            print('w_n1', u_N_n1.dtype, u_N_n1.shape)
            print('s_x_n1', u_Tx_n1.dtype, u_Tx_n1.shape)
            print('s_y_n1', u_Ty_n1.dtype, u_Ty_n1.shape)
            print('Eps_k', Eps_k.dtype, Eps_k.shape)
            print('Sig_k', Sig_k.dtype, Sig_k.shape)
        ONES = np.ones_like(u_Tx_n1, dtype=np.float_)
        if self.debug:
            print('ONES', ONES.dtype)
        ZEROS = np.zeros_like(u_Tx_n1, dtype=np.float_)
        if self.debug:
            print('ZEROS', ZEROS.dtype)
        Sig_k = self.symb.get_Sig_(u_N_n1, u_Tx_n1, u_Ty_n1, Sig_k, Eps_k)[0]
        if self.debug:
            print('Sig_k', Sig_k.dtype, Sig_k.shape)
        dSig_dEps_k = self.symb.get_dSig_dEps_(u_N_n1, u_Tx_n1, u_Ty_n1, Sig_k,
                                               Eps_k, ZEROS, ONES)
        if self.debug:
            print('dSig_dEps_k', dSig_dEps_k.dtype)
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_k)
        if self.debug:
            print('H_sig_pi', H_sig_pi.dtype)
        f_k = np.array([self.symb.get_f_(Eps_k, Sig_k, H_sig_pi)])
        if self.debug:
            print('f_k', f_k.dtype)
        df_dSig_k = self.symb.get_df_dSig_(Eps_k, Sig_k, H_sig_pi, ZEROS, ONES)
        if self.debug:
            print('df_dSig_k', df_dSig_k.dtype)
        ddf_dEps_k = self.symb.get_ddf_dEps_(Eps_k, Sig_k, H_sig_pi, ZEROS,
                                             ONES)
        if self.debug:
            print('ddf_dEps_k', ddf_dEps_k.dtype)
        df_dEps_k = np.einsum('ik...,ji...->jk...', df_dSig_k,
                              dSig_dEps_k) + ddf_dEps_k
        Phi_k = self.get_Phi_(Eps_k, Sig_k, H_sig_pi, ZEROS, ONES)
        dEps_dlambda_k = Phi_k
        df_dlambda = np.einsum('ki...,kj...->ij...', df_dEps_k, dEps_dlambda_k)
        df_k = df_dlambda
        return f_k, df_k, Sig_k

    def get_Eps_k1(self, u_N_n1, u_Tx_n1, u_Ty_n1, Eps_n, lam_k, Sig_k, Eps_k):
        '''Evolution equations:
        The update of state variables
        for an updated $\lambda_k$ is performed using this procedure.
        '''
        ONES = np.ones_like(u_Tx_n1)
        ZEROS = np.zeros_like(u_Tx_n1)
        Sig_k = self.symb.get_Sig_(u_N_n1, u_Tx_n1, u_Ty_n1, Sig_k, Eps_k)[0]
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_k)
        Phi_k = self.get_Phi_(Eps_k, Sig_k, H_sig_pi, ZEROS, ONES)
        Eps_k1 = Eps_n + lam_k * Phi_k[:, 0]
        return Eps_k1

    rtol = bu.Float(1e-3, ALG=True)
    '''Relative tolerance of the return mapping algorithm related 
    to the tensile strength
    '''

    Eps_names = tr.Property

    @tr.cached_property
    def _get_Eps_names(self):
        return [eps.codename for eps in self.symb.Eps]

    Sig_names = tr.Property

    @tr.cached_property
    def _get_Sig_names(self):
        return [sig.codename for sig in self.symb.Sig]

    state_var_shapes = tr.Property

    @tr.cached_property
    def _get_state_var_shapes(self):
        '''State variables shapes:
        variables are using the codename string in the Cymbol definition
        Since the same string is used in the lambdify method via print_Symbol
        method defined in Cymbol as well'''
        return {eps_name: () for eps_name in self.Eps_names + self.Sig_names}

    k_max = bu.Int(100, ALG=True)
    '''Maximum number of iterations'''

    def get_corr_pred(self, eps_Ema, t_n1, **state):
        '''Return mapping iteration:
        This function represents a user subroutine in a finite element
        code or in a lattice model. The input is $s_{n+1}$ and the state variables
        representing the state in the previous solved step $\boldsymbol{\mathcal{E}}_n$.
        The procedure returns the stresses and state variables of
        $\boldsymbol{\mathcal{S}}_{n+1}$ and $\boldsymbol{\mathcal{E}}_{n+1}$
        '''
        eps_aEm = np.einsum('...a->a...', eps_Ema)
        dim = len(eps_aEm)

        if dim == 2:  # hack - only one slip considered - 2D version
            select_idx = (1, 0)
            u_Tx_n1, u_N_n1 = eps_aEm
            u_Ty_n1 = np.zeros_like(u_Tx_n1)
        else:
            raise ValueError('3D not implemented here')

        ONES = np.ones_like(u_Tx_n1, dtype=np.float_)
        if self.debug:
            print('ONES', ONES.dtype)
        ZEROS = np.zeros_like(u_Tx_n1, dtype=np.float_)
        if self.debug:
            print('ZEROS', ZEROS.dtype)

        # Transform state to Eps_k and Sig_k
        Eps_n = np.array([state[eps_name] for eps_name in self.Eps_names],
                         dtype=np.float_)
        Eps_k = np.copy(Eps_n)
        #Sig_k = np.array([state[sig_name] for sig_name in self.Sig_names], dtype=np.float_)
        Sig_k = np.zeros_like(Eps_k)
        f_k, df_k, Sig_k = self.get_f_df(u_N_n1, u_Tx_n1, u_Ty_n1, Sig_k,
                                         Eps_k)
        f_k, df_k = f_k[0, ...], df_k[0, 0, ...]
        f_k_trial = f_k
        # indexes of inelastic entries
        L = np.where(f_k_trial > 0)
        # f norm in inelastic entries - to allow also positive values less the rtol
        f_k_norm_I = np.fabs(f_k_trial[L])
        lam_k = np.zeros_like(f_k_trial)
        k = 0
        while k < self.k_max:
            if self.debug:
                print('k', k)
            # which entries are above the tolerance
            I = np.where(f_k_norm_I > (self.f_t * self.rtol))
            if self.debug:
                print('f_k_norm_I', f_k_norm_I, self.f_t * self.rtol,
                      len(I[0]))
            if (len(I[0]) == 0):
                # empty inelastic entries - accept state
                #return Eps_k, Sig_k, k + 1
                dSig_dEps_k = self.symb.get_dSig_dEps_(u_N_n1, u_Tx_n1,
                                                       u_Ty_n1, Sig_k, Eps_k,
                                                       ZEROS, ONES)
                ix1, ix2 = np.ix_(select_idx, select_idx)
                D_ = np.einsum('ab...->...ab', dSig_dEps_k[ix1, ix2, ...])
                sig_ = np.einsum('a...->...a', Sig_k[select_idx, ...])
                # quick fix
                _, _, _, _, _, _, omega_T, omega_N = Eps_k
                D_ = np.zeros(sig_.shape + (sig_.shape[-1], ))
                D_[..., 0, 0] = self.E_N * (1 - omega_N)
                D_[..., 1, 1] = self.E_T * (1 - omega_T)
                if dim == 3:
                    D_[..., 2, 2] = self.E_T  #* (1 - omega_T)
                for eps_name, Eps_ in zip(self.Eps_names, Eps_k):
                    state[eps_name][...] = Eps_[...]
                for sig_name, Sig_ in zip(self.Sig_names, Sig_k):
                    state[sig_name][...] = Sig_[...]
                return sig_, D_

            if self.debug:
                print('I', I)
                print('L', L)
            LL = tuple(Li[I] for Li in L)
            L = LL
            if self.debug:
                print('new L', L)
                print('f_k', f_k[L].shape, f_k[L].dtype)
                print('df_k', df_k[L].shape, df_k[L].dtype)
            # return mapping on inelastic entries
            dlam_L = -f_k[L] / df_k[L]  # np.linalg.solve(df_k[I], -f_k[I])
            if self.debug:
                print('dlam_I', dlam_L, dlam_L.dtype)
            lam_k[L] += dlam_L
            if self.debug:
                print('lam_k_L', lam_k, lam_k.dtype, lam_k[L].shape)
            L_slice = (slice(None), ) + L
            Eps_k_L = self.get_Eps_k1(u_N_n1[L], u_Tx_n1[L], u_Ty_n1[L],
                                      Eps_n[L_slice], lam_k[L], Sig_k[L_slice],
                                      Eps_k[L_slice])
            Eps_k[L_slice] = Eps_k_L
            f_k_L, df_k_L, Sig_k_L = self.get_f_df(u_N_n1[L], u_Tx_n1[L],
                                                   u_Ty_n1[L], Sig_k[L_slice],
                                                   Eps_k_L)
            f_k[L], df_k[L] = f_k_L[0, ...], df_k_L[0, 0, ...]
            Sig_k[L_slice] = Sig_k_L
            if self.debug:
                print('Sig_k', Sig_k)
                print('f_k', f_k)
            f_k_norm_I = np.fabs(f_k[L])
            k += 1
        else:
            raise ConvergenceError('no convergence for entries',
                                   [L, u_N_n1[I], u_Tx_n1[I], u_Ty_n1[I]])
        # add the algorithmic stiffness
        # recalculate df_k and -f_k for a unit increment of epsilon and solve for lambda
        #

    def plot_f_state(self, ax, Eps, Sig):
        lower = -self.f_c * 1.05
        upper = self.f_t + 0.05 * self.f_c
        lower_tau = -self.bartau * 2
        upper_tau = self.bartau * 2
        lower_tau = 0
        upper_tau = 10
        sig, tau_x, tau_y = Sig[:3]
        tau = np.sqrt(tau_x**2 + tau_y**2)
        sig_ts, tau_x_ts = np.mgrid[lower:upper:201j, lower_tau:upper_tau:201j]
        Sig_ts = np.zeros((len(self.symb.Eps), ) + tau_x_ts.shape)
        Eps_ts = np.zeros_like(Sig_ts)
        Sig_ts[0, ...] = sig_ts
        Sig_ts[1, ...] = tau_x_ts
        Sig_ts[3:, ...] = Sig[3:, np.newaxis, np.newaxis]
        Eps_ts[...] = Eps[:, np.newaxis, np.newaxis]
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_ts)
        f_ts = np.array([self.symb.get_f_(Eps_ts, Sig_ts, H_sig_pi)])

        #phi_ts = np.array([self.symb.get_phi_(Eps_ts, Sig_ts)])
        ax.set_title('threshold function')

        omega_N = Eps_ts[-1, :]
        omega_T = Eps_ts[-2, :]
        sig_ts_eff = sig_ts / (1 - H_sig_pi * omega_N)
        tau_x_ts_eff = tau_x_ts / (1 - omega_T)
        ax.contour(sig_ts_eff,
                   tau_x_ts_eff,
                   f_ts[0, ...],
                   levels=0,
                   colors=('green', ))

        ax.contour(sig_ts, tau_x_ts, f_ts[0, ...], levels=0, colors=('red', ))
        #ax.contour(sig_ts, tau_x_ts, phi_ts[0, ...])
        ax.plot(sig, tau, marker='H', color='red')
        ax.plot([lower, upper], [0, 0], color='black', lw=0.4)
        ax.plot([0, 0], [lower_tau, upper_tau], color='black', lw=0.4)
        ax.set_ylim(ymin=0, ymax=10)

    def plot_f(self, ax):
        lower = -self.f_c * 1.05
        upper = self.f_t + 0.05 * self.f_c
        lower_tau = -self.bartau * 2
        upper_tau = self.bartau * 2
        sig_ts, tau_x_ts = np.mgrid[lower:upper:201j, lower_tau:upper_tau:201j]
        Sig_ts = np.zeros((len(self.symb.Eps), ) + tau_x_ts.shape)
        Sig_ts[0, :] = sig_ts
        Sig_ts[1, :] = tau_x_ts
        Eps_ts = np.zeros_like(Sig_ts)
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_ts)
        f_ts = np.array([self.symb.get_f_(Eps_ts, Sig_ts, H_sig_pi)])
        phi_ts = np.array([self.get_phi_(Eps_ts, Sig_ts, H_sig_pi)])
        ax.set_title('threshold function')
        ax.contour(sig_ts, tau_x_ts, f_ts[0, ...], levels=0)
        ax.contour(sig_ts, tau_x_ts, phi_ts[0, ...])
        ax.plot([lower, upper], [0, 0], color='black', lw=0.4)
        ax.plot([0, 0], [lower_tau, upper_tau], color='black', lw=0.4)

    def plot_sig_w(self, ax):
        pass

    def plot_tau_s(self, ax):
        pass

    def subplots(self, fig):
        return fig.subplots(2, 2)

    def update_plot(self, axes):
        (ax_sig_w, ax_tau_s), (ax_f, _) = axes
        self.plot_sig_w(ax_sig_w)
        self.plot_tau_s(ax_tau_s)
        self.plot_f(ax_f)
Beispiel #28
0
class Parameter(t.HasTraits):
    """Model parameter

    Attributes
    ----------
    value : float or array
        The value of the parameter for the current location. The value
        for other locations is stored in map.
    bmin, bmax: float
        Lower and upper bounds of the parameter value.
    twin : {None, Parameter}
        If it is not None, the value of the current parameter is
        a function of the given Parameter. The function is by default
        the identity function, but it can be defined by twin_function
    twin_function_expr: str
        Expression of the ``twin_function`` that enables setting a functional
        relationship between the parameter and its twin. If ``twin`` is not
        ``None``, the parameter value is calculated as the output of calling the
        twin function with the value of the twin parameter. The string is
        parsed using sympy, so permitted values are any valid sympy expressions
        of one variable. If the function is invertible the twin inverse function
        is set automatically.
    twin_inverse_function : str
        Expression of the ``twin_inverse_function`` that enables setting the
        value of the twin parameter. If ``twin`` is not
        ``None``, its value is set to the output of calling the
        twin inverse function with the value provided. The string is
        parsed using sympy, so permitted values are any valid sympy expressions
        of one variable.
    twin_function : function
        **Setting this attribute manually
        is deprecated in HyperSpy newer than 1.1.2. It will become private in
        HyperSpy 2.0. Please use ``twin_function_expr`` instead.**
    twin_inverse_function : function
        **Setting this attribute manually
        is deprecated in HyperSpy newer than 1.1.2. It will become private in
        HyperSpy 2.0. Please use ``twin_inverse_function_expr`` instead.**
    ext_force_positive : bool
        If True, the parameter value is set to be the absolute value
        of the input value i.e. if we set Parameter.value = -3, the
        value stored is 3 instead. This is useful to bound a value
        to be positive in an optimization without actually using an
        optimizer that supports bounding.
    ext_bounded : bool
        Similar to ext_force_positive, but in this case the bounds are
        defined by bmin and bmax. It is a better idea to use
        an optimizer that supports bounding though.

    Methods
    -------
    as_signal(field = 'values')
        Get a parameter map as a signal object
    plot()
        Plots the value of the Parameter at all locations.
    export(folder=None, name=None, format=None, save_std=False)
        Saves the value of the parameter map to the specified format
    connect, disconnect(function)
        Call the functions connected when the value attribute changes.

    """
    __number_of_elements = 1
    __value = 0
    __free = True
    _bounds = (None, None)
    __twin = None
    _axes_manager = None
    __ext_bounded = False
    __ext_force_positive = False

    # traitsui bugs out trying to make an editor for this, so always specify!
    # (it bugs out, because both editor shares the object, and Array editors
    # don't like non-sequence objects). TextEditor() works well, so does
    # RangeEditor() as it works with bmin/bmax.
    value = t.Property(t.Either([t.CFloat(0), Array()]))

    units = t.Str('')
    free = t.Property(t.CBool(True))

    bmin = t.Property(NoneFloat(), label="Lower bounds")
    bmax = t.Property(NoneFloat(), label="Upper bounds")
    _twin_function_expr = ""
    _twin_inverse_function_expr = ""
    twin_function = None
    _twin_inverse_function = None
    _twin_inverse_sympy = None

    def __init__(self):
        self._twins = set()
        self.events = Events()
        self.events.value_changed = Event("""
            Event that triggers when the `Parameter.value` changes.

            The event triggers after the internal state of the `Parameter` has
            been updated.

            Arguments
            ---------
            obj : Parameter
                The `Parameter` that the event belongs to
            value : {float | array}
                The new value of the parameter
            """,
                                          arguments=["obj", 'value'])
        self.std = None
        self.component = None
        self.grad = None
        self.name = ''
        self.units = ''
        self.map = None
        self.model = None
        self._whitelist = {
            '_id_name': None,
            'value': None,
            'std': None,
            'free': None,
            'units': None,
            'map': None,
            '_bounds': None,
            'ext_bounded': None,
            'name': None,
            'ext_force_positive': None,
            'twin_function_expr': None,
            'twin_inverse_function_expr': None,
            'self': ('id', None),
        }
        self._slicing_whitelist = {'map': 'inav'}

    def _load_dictionary(self, dictionary):
        """Load data from dictionary

        Parameters
        ----------
        dict : dictionary
            A dictionary containing at least the following items:
            _id_name : string
                _id_name of the original parameter, used to create the
                dictionary. Has to match with the self._id_name
            _whitelist : dictionary
                a dictionary, which keys are used as keywords to match with the
                parameter attributes.  For more information see
                :meth:`hyperspy.misc.export_dictionary.load_from_dictionary`
            * any field from _whitelist.keys() *
        Returns
        -------
        id_value : int
            the ID value of the original parameter, to be later used for setting
            up the correct twins

        """
        if dictionary['_id_name'] == self._id_name:
            load_from_dictionary(self, dictionary)
            return dictionary['self']
        else:
            raise ValueError(
                "_id_name of parameter and dictionary do not match, \nparameter._id_name = %s\
                    \ndictionary['_id_name'] = %s" %
                (self._id_name, dictionary['_id_name']))

    def __repr__(self):
        text = ''
        text += 'Parameter %s' % self.name
        if self.component is not None:
            text += ' of %s' % self.component._get_short_description()
        text = '<' + text + '>'
        return text

    def __len__(self):
        return self._number_of_elements

    @property
    def twin_function_expr(self):
        return self._twin_function_expr

    @twin_function_expr.setter
    def twin_function_expr(self, value):
        if not value:
            self.twin_function = None
            self.twin_inverse_function = None
            self._twin_function_expr = ""
            self._twin_inverse_sympy = None
            return
        expr = sympy.sympify(value)
        if len(expr.free_symbols) > 1:
            raise ValueError("The expression must contain only one variable.")
        elif len(expr.free_symbols) == 0:
            raise ValueError("The expression must contain one variable, "
                             "it contains none.")
        x = tuple(expr.free_symbols)[0]
        self.twin_function = lambdify(x, expr.evalf())
        self._twin_function_expr = value
        if not self.twin_inverse_function:
            y = sympy.Symbol(x.name + "2")
            try:
                inv = sympy.solveset(sympy.Eq(y, expr), x)
                self._twin_inverse_sympy = lambdify(y, inv)
                self._twin_inverse_function = None
            except BaseException:
                # Not all may have a suitable solution.
                self._twin_inverse_function = None
                self._twin_inverse_sympy = None
                _logger.warning(
                    "The function {} is not invertible. Setting the value of "
                    "{} will raise an AttributeError unless you set manually "
                    "``twin_inverse_function_expr``. Otherwise, set the "
                    "value of its twin parameter instead.".format(value, self))

    @property
    def twin_inverse_function_expr(self):
        if self.twin:
            return self._twin_inverse_function_expr
        else:
            return ""

    @twin_inverse_function_expr.setter
    def twin_inverse_function_expr(self, value):
        if not value:
            self.twin_inverse_function = None
            self._twin_inverse_function_expr = ""
            return
        expr = sympy.sympify(value)
        if len(expr.free_symbols) > 1:
            raise ValueError("The expression must contain only one variable.")
        elif len(expr.free_symbols) == 0:
            raise ValueError("The expression must contain one variable, "
                             "it contains none.")
        x = tuple(expr.free_symbols)[0]
        self._twin_inverse_function = lambdify(x, expr.evalf())
        self._twin_inverse_function_expr = value

    @property
    def twin_inverse_function(self):
        if (not self.twin_inverse_function_expr and self.twin_function_expr
                and self._twin_inverse_sympy):
            return lambda x: self._twin_inverse_sympy(x).pop()
        else:
            return self._twin_inverse_function

    @twin_inverse_function.setter
    def twin_inverse_function(self, value):
        self._twin_inverse_function = value

    def _get_value(self):
        if self.twin is None:
            return self.__value
        else:
            if self.twin_function:
                return self.twin_function(self.twin.value)
            else:
                return self.twin.value

    def _set_value(self, value):
        try:
            # Use try/except instead of hasattr("__len__") because a numpy
            # memmap has a __len__ wrapper even for numbers that raises a
            # TypeError when calling. See issue #349.
            if len(value) != self._number_of_elements:
                raise ValueError("The length of the parameter must be ",
                                 self._number_of_elements)
            else:
                if not isinstance(value, tuple):
                    value = tuple(value)
        except TypeError:
            if self._number_of_elements != 1:
                raise ValueError("The length of the parameter must be ",
                                 self._number_of_elements)
        old_value = self.__value

        if self.twin is not None:
            if self.twin_function is not None:
                if self.twin_inverse_function is not None:
                    self.twin.value = self.twin_inverse_function(value)
                    return
                else:
                    raise AttributeError(
                        "This parameter has a ``twin_function`` but"
                        "its ``twin_inverse_function`` is not defined.")
            else:
                self.twin.value = value
                return

        if self.ext_bounded is False:
            self.__value = value
        else:
            if self.ext_force_positive is True:
                value = np.abs(value)
            if self._number_of_elements == 1:
                if self.bmin is not None and value <= self.bmin:
                    self.__value = self.bmin
                elif self.bmax is not None and value >= self.bmax:
                    self.__value = self.bmax
                else:
                    self.__value = value
            else:
                bmin = (self.bmin if self.bmin is not None else -np.inf)
                bmax = (self.bmax if self.bmin is not None else np.inf)
                self.__value = np.clip(value, bmin, bmax)

        if (self._number_of_elements != 1
                and not isinstance(self.__value, tuple)):
            self.__value = tuple(self.__value)
        if old_value != self.__value:
            self.events.value_changed.trigger(value=self.__value, obj=self)
        self.trait_property_changed('value', old_value, self.__value)

    # Fix the parameter when coupled
    def _get_free(self):
        if self.twin is None:
            return self.__free
        else:
            return False

    def _set_free(self, arg):
        old_value = self.__free
        self.__free = arg
        if self.component is not None:
            self.component._update_free_parameters()
        self.trait_property_changed('free', old_value, self.__free)

    def _on_twin_update(self, value, twin=None):
        if (twin is not None and hasattr(twin, 'events')
                and hasattr(twin.events, 'value_changed')):
            with twin.events.value_changed.suppress_callback(
                    self._on_twin_update):
                self.events.value_changed.trigger(value=value, obj=self)
        else:
            self.events.value_changed.trigger(value=value, obj=self)

    def _set_twin(self, arg):
        if arg is None:
            if self.twin is not None:
                # Store the value of the twin in order to set the
                # value of the parameter when it is uncoupled
                twin_value = self.value
                if self in self.twin._twins:
                    self.twin._twins.remove(self)
                    self.twin.events.value_changed.disconnect(
                        self._on_twin_update)

                self.__twin = arg
                self.value = twin_value
        else:
            if self not in arg._twins:
                arg._twins.add(self)
                arg.events.value_changed.connect(self._on_twin_update,
                                                 ["value"])
            self.__twin = arg

        if self.component is not None:
            self.component._update_free_parameters()

    def _get_twin(self):
        return self.__twin

    twin = property(_get_twin, _set_twin)

    def _get_bmin(self):
        if self._number_of_elements == 1:
            return self._bounds[0]
        else:
            return self._bounds[0][0]

    def _set_bmin(self, arg):
        old_value = self.bmin
        if self._number_of_elements == 1:
            self._bounds = (arg, self.bmax)
        else:
            self._bounds = ((arg, self.bmax), ) * self._number_of_elements
        # Update the value to take into account the new bounds
        self.value = self.value
        self.trait_property_changed('bmin', old_value, arg)

    def _get_bmax(self):
        if self._number_of_elements == 1:
            return self._bounds[1]
        else:
            return self._bounds[0][1]

    def _set_bmax(self, arg):
        old_value = self.bmax
        if self._number_of_elements == 1:
            self._bounds = (self.bmin, arg)
        else:
            self._bounds = ((self.bmin, arg), ) * self._number_of_elements
        # Update the value to take into account the new bounds
        self.value = self.value
        self.trait_property_changed('bmax', old_value, arg)

    @property
    def _number_of_elements(self):
        return self.__number_of_elements

    @_number_of_elements.setter
    def _number_of_elements(self, arg):
        # Do nothing if the number of arguments stays the same
        if self.__number_of_elements == arg:
            return
        if arg <= 1:
            raise ValueError("Please provide an integer number equal "
                             "or greater to 1")
        self._bounds = ((self.bmin, self.bmax), ) * arg
        self.__number_of_elements = arg

        if arg == 1:
            self._Parameter__value = 0
        else:
            self._Parameter__value = (0, ) * arg
        if self.component is not None:
            self.component.update_number_parameters()

    @property
    def ext_bounded(self):
        return self.__ext_bounded

    @ext_bounded.setter
    def ext_bounded(self, arg):
        if arg is not self.__ext_bounded:
            self.__ext_bounded = arg
            # Update the value to take into account the new bounds
            self.value = self.value

    @property
    def ext_force_positive(self):
        return self.__ext_force_positive

    @ext_force_positive.setter
    def ext_force_positive(self, arg):
        if arg is not self.__ext_force_positive:
            self.__ext_force_positive = arg
            # Update the value to take into account the new bounds
            self.value = self.value

    def store_current_value_in_array(self):
        """Store the value and std attributes.

        See also
        --------
        fetch, assign_current_value_to_all

        """
        indices = self._axes_manager.indices[::-1]
        # If it is a single spectrum indices is ()
        if not indices:
            indices = (0, )
        self.map['values'][indices] = self.value
        self.map['is_set'][indices] = True
        if self.std is not None:
            self.map['std'][indices] = self.std

    def fetch(self):
        """Fetch the stored value and std attributes.


        See Also
        --------
        store_current_value_in_array, assign_current_value_to_all

        """
        indices = self._axes_manager.indices[::-1]
        # If it is a single spectrum indices is ()
        if not indices:
            indices = (0, )
        if self.map['is_set'][indices]:
            value = self.map['values'][indices]
            std = self.map['std'][indices]
            if isinstance(value, dArray):
                value = value.compute()
            if isinstance(std, dArray):
                std = std.compute()
            self.value = value
            self.std = std

    def assign_current_value_to_all(self, mask=None):
        """Assign the current value attribute to all the  indices

        Parameters
        ----------
        mask: {None, boolean numpy array}
            Set only the indices that are not masked i.e. where
            mask is False.

        See Also
        --------
        store_current_value_in_array, fetch

        """
        if mask is None:
            mask = np.zeros(self.map.shape, dtype='bool')
        self.map['values'][mask == False] = self.value
        self.map['is_set'][mask == False] = True

    def _create_array(self):
        """Create the map array to store the information in
        multidimensional datasets.

        """
        shape = self._axes_manager._navigation_shape_in_array
        if not shape:
            shape = [
                1,
            ]
        # Shape-1 fields in dtypes won’t be collapsed to scalars in a future
        # numpy version (see release notes numpy 1.17.0)
        if self._number_of_elements > 1:
            dtype_ = np.dtype([('values', 'float', self._number_of_elements),
                               ('std', 'float', self._number_of_elements),
                               ('is_set', 'bool')])
        else:
            dtype_ = np.dtype([('values', 'float'), ('std', 'float'),
                               ('is_set', 'bool')])
        if (self.map is None or self.map.shape != shape
                or self.map.dtype != dtype_):
            self.map = np.zeros(shape, dtype_)
            self.map['std'].fill(np.nan)
            # TODO: in the future this class should have access to
            # axes manager and should be able to fetch its own
            # values. Until then, the next line is necessary to avoid
            # erros when self.std is defined and the shape is different
            # from the newly defined arrays
            self.std = None

    def as_signal(self, field='values'):
        """Get a parameter map as a signal object.

        Please note that this method only works when the navigation
        dimension is greater than 0.

        Parameters
        ----------
        field : {'values', 'std', 'is_set'}

        Raises
        ------

        NavigationDimensionError : if the navigation dimension is 0

        """
        from hyperspy.signal import BaseSignal

        s = BaseSignal(data=self.map[field],
                       axes=self._axes_manager._get_navigation_axes_dicts())
        if self.component is not None and \
                self.component.active_is_multidimensional:
            s.data[np.logical_not(self.component._active_array)] = np.nan

        s.metadata.General.title = ("%s parameter" %
                                    self.name if self.component is None else
                                    "%s parameter of %s component" %
                                    (self.name, self.component.name))
        for axis in s.axes_manager._axes:
            axis.navigate = False
        if self._number_of_elements > 1:
            s.axes_manager._append_axis(size=self._number_of_elements,
                                        name=self.name,
                                        navigate=True)
        s._assign_subclass()
        if field == "values":
            # Add the variance if available
            std = self.as_signal(field="std")
            if not np.isnan(std.data).all():
                std.data = std.data**2
                std.metadata.General.title = "Variance"
                s.metadata.set_item("Signal.Noise_properties.variance", std)
        return s

    def plot(self, **kwargs):
        """Plot parameter signal.

        Parameters
        ----------
        **kwargs
            Any extra keyword arguments are passed to the signal plot.

        Example
        -------
        >>> parameter.plot() #doctest: +SKIP

        Set the minimum and maximum displayed values

        >>> parameter.plot(vmin=0, vmax=1) #doctest: +SKIP
        """
        self.as_signal().plot(**kwargs)

    def export(self, folder=None, name=None, format="hspy", save_std=False):
        """Save the data to a file.

        All the arguments are optional.

        Parameters
        ----------
        folder : str or None
            The path to the folder where the file will be saved.
             If `None` the current folder is used by default.
        name : str or None
            The name of the file. If `None` the Components name followed
             by the Parameter `name` attributes will be used by default.
              If a file with the same name exists the name will be
              modified by appending a number to the file path.
        save_std : bool
            If True, also the standard deviation will be saved
        format: str
            The extension of any file format supported by HyperSpy, default hspy

        """
        if format is None:
            format = "hspy"
        if name is None:
            name = self.component.name + '_' + self.name
        filename = incremental_filename(slugify(name) + '.' + format)
        if folder is not None:
            filename = os.path.join(folder, filename)
        self.as_signal().save(filename)
        if save_std is True:
            self.as_signal(field='std').save(append2pathname(filename, '_std'))

    def as_dictionary(self, fullcopy=True):
        """Returns parameter as a dictionary, saving all attributes from
        self._whitelist.keys() For more information see
        :meth:`hyperspy.misc.export_dictionary.export_to_dictionary`

        Parameters
        ----------
        fullcopy : Bool (optional, False)
            Copies of objects are stored, not references. If any found,
            functions will be pickled and signals converted to dictionaries
        Returns
        -------
        dic : dictionary with the following keys:
            _id_name : string
                _id_name of the original parameter, used to create the
                dictionary. Has to match with the self._id_name
            _twins : list
                a list of ids of the twins of the parameter
            _whitelist : dictionary
                a dictionary, which keys are used as keywords to match with the
                parameter attributes.  For more information see
                :meth:`hyperspy.misc.export_dictionary.export_to_dictionary`
            * any field from _whitelist.keys() *

        """
        dic = {'_twins': [id(t) for t in self._twins]}
        export_to_dictionary(self, self._whitelist, dic, fullcopy)
        return dic

    def default_traits_view(self):
        # As mentioned above, the default editor for
        # value = t.Property(t.Either([t.CFloat(0), Array()]))
        # gives a ValueError. We therefore implement default_traits_view so
        # that configure/edit_traits will still work straight out of the box.
        # A whitelist controls which traits to include in this view.
        from traitsui.api import RangeEditor, View, Item
        whitelist = ['bmax', 'bmin', 'free', 'name', 'std', 'units', 'value']
        editable_traits = [
            trait for trait in self.editable_traits() if trait in whitelist
        ]
        if 'value' in editable_traits:
            i = editable_traits.index('value')
            v = editable_traits.pop(i)
            editable_traits.insert(
                i,
                Item(v, editor=RangeEditor(low_name='bmin', high_name='bmax')))
        view = View(editable_traits, buttons=['OK', 'Cancel'])
        return view
Beispiel #29
0
class Component(t.HasTraits):
    __axes_manager = None

    active = t.Property(t.CBool(True))
    name = t.Property(t.Str(''))

    def __init__(self, parameter_name_list):
        self.events = Events()
        self.events.active_changed = Event("""
            Event that triggers when the `Component.active` changes.

            The event triggers after the internal state of the `Component` has
            been updated.

            Arguments
            ---------
            obj : Component
                The `Component` that the event belongs to
            active : bool
                The new active state
            """,
                                           arguments=["obj", 'active'])
        self.parameters = []
        self.init_parameters(parameter_name_list)
        self._update_free_parameters()
        self.active = True
        self._active_array = None
        self.isbackground = False
        self.convolved = True
        self.parameters = tuple(self.parameters)
        self._id_name = self.__class__.__name__
        self._id_version = '1.0'
        self._position = None
        self.model = None
        self.name = ''
        self._whitelist = {
            '_id_name': None,
            'name': None,
            'active_is_multidimensional': None,
            '_active_array': None,
            'active': None
        }
        self._slicing_whitelist = {'_active_array': 'inav'}
        self._slicing_order = (
            'active',
            'active_is_multidimensional',
            '_active_array',
        )

    _name = ''
    _active_is_multidimensional = False
    _active = True

    @property
    def active_is_multidimensional(self):
        return self._active_is_multidimensional

    @active_is_multidimensional.setter
    def active_is_multidimensional(self, value):
        if not isinstance(value, bool):
            raise ValueError('Only boolean values are permitted')

        if value == self.active_is_multidimensional:
            return

        if value:  # Turn on
            if self._axes_manager.navigation_size < 2:
                _logger.info('`navigation_size` < 2, skipping')
                return
            # Store value at current position
            self._create_active_array()
            self._store_active_value_in_array(self._active)
            self._active_is_multidimensional = True
        else:  # Turn off
            # Get the value at the current position before switching it off
            self._active = self.active
            self._active_array = None
            self._active_is_multidimensional = False

    def _get_name(self):
        return self._name

    def _set_name(self, value):
        old_value = self._name
        if old_value == value:
            return
        if self.model:
            for component in self.model:
                if value == component.name:
                    raise ValueError("Another component already has "
                                     "the name " + str(value))
            self._name = value
            setattr(self.model.components,
                    slugify(value, valid_variable_name=True), self)
            self.model.components.__delattr__(
                slugify(old_value, valid_variable_name=True))
        else:
            self._name = value
        self.trait_property_changed('name', old_value, self._name)

    @property
    def _axes_manager(self):
        return self.__axes_manager

    @_axes_manager.setter
    def _axes_manager(self, value):
        for parameter in self.parameters:
            parameter._axes_manager = value
        self.__axes_manager = value

    @property
    def _is_navigation_multidimensional(self):
        if (self._axes_manager is None
                or not self._axes_manager.navigation_dimension):
            return False
        else:
            return True

    def _get_active(self):
        if self.active_is_multidimensional is True:
            # The following should set
            self.active = self._active_array[self._axes_manager.indices[::-1]]
        return self._active

    def _store_active_value_in_array(self, value):
        self._active_array[self._axes_manager.indices[::-1]] = value

    def _set_active(self, arg):
        if self._active == arg:
            return
        old_value = self._active
        self._active = arg
        if self.active_is_multidimensional is True:
            self._store_active_value_in_array(arg)
        self.events.active_changed.trigger(active=self._active, obj=self)
        self.trait_property_changed('active', old_value, self._active)

    def init_parameters(self, parameter_name_list):
        for name in parameter_name_list:
            parameter = Parameter()
            self.parameters.append(parameter)
            parameter.name = name
            parameter._id_name = name
            setattr(self, name, parameter)
            if hasattr(self, 'grad_' + name):
                parameter.grad = getattr(self, 'grad_' + name)
            parameter.component = self
            self.add_trait(name, t.Instance(Parameter))

    def _get_long_description(self):
        if self.name:
            text = '%s (%s component)' % (self.name, self.__class__.__name__)
        else:
            text = '%s component' % self.__class__.__name__
        return text

    def _get_short_description(self):
        text = ''
        if self.name:
            text += self.name
        else:
            text += self.__class__.__name__
        text += ' component'
        return text

    def __repr__(self):
        text = '<%s>' % self._get_long_description()
        return text

    def _update_free_parameters(self):
        self.free_parameters = sorted(
            [par for par in self.parameters if par.free], key=lambda x: x.name)
        self._nfree_param = sum(
            [par._number_of_elements for par in self.free_parameters])

    def update_number_parameters(self):
        i = 0
        for parameter in self.parameters:
            i += parameter._number_of_elements
        self.nparam = i
        self._update_free_parameters()

    def fetch_values_from_array(self, p, p_std=None, onlyfree=False):
        if onlyfree is True:
            parameters = self.free_parameters
        else:
            parameters = self.parameters
        i = 0
        for parameter in sorted(parameters, key=lambda x: x.name):
            length = parameter._number_of_elements
            parameter.value = (p[i] if length == 1 else p[i:i + length])
            if p_std is not None:
                parameter.std = (p_std[i] if length == 1 else tuple(
                    p_std[i:i + length]))

            i += length

    def _create_active_array(self):
        shape = self._axes_manager._navigation_shape_in_array
        if len(shape) == 1 and shape[0] == 0:
            shape = [
                1,
            ]
        if (not isinstance(self._active_array, np.ndarray)
                or self._active_array.shape != shape):
            _logger.debug('Creating _active_array for {}.\n\tCurrent array '
                          'is:\n{}'.format(self, self._active_array))
            self._active_array = np.ones(shape, dtype=bool)

    def _create_arrays(self):
        if self.active_is_multidimensional:
            self._create_active_array()
        for parameter in self.parameters:
            parameter._create_array()

    def store_current_parameters_in_map(self):
        for parameter in self.parameters:
            parameter.store_current_value_in_array()

    def fetch_stored_values(self, only_fixed=False):
        if self.active_is_multidimensional:
            # Store the stored value in self._active and trigger the connected
            # functions.
            self.active = self.active
        if only_fixed is True:
            parameters = (set(self.parameters) - set(self.free_parameters))
        else:
            parameters = self.parameters
        parameters = [
            parameter for parameter in parameters
            if (parameter.twin is None
                or not isinstance(parameter.twin, Parameter))
        ]
        for parameter in parameters:
            parameter.fetch()

    def plot(self, only_free=True):
        """Plot the value of the parameters of the model

        Parameters
        ----------
        only_free : bool
            If True, only the value of the parameters that are free will
             be plotted

        """
        if only_free:
            parameters = self.free_parameters
        else:
            parameters = self.parameters

        parameters = [k for k in parameters if k.twin is None]
        for parameter in parameters:
            parameter.plot()

    def export(self,
               folder=None,
               format="hspy",
               save_std=False,
               only_free=True):
        """Plot the value of the parameters of the model

        Parameters
        ----------
        folder : str or None
            The path to the folder where the file will be saved. If
            `None` the
            current folder is used by default.
        format : str
            The extension of the file format, default "hspy".
        save_std : bool
            If True, also the standard deviation will be saved.
        only_free : bool
            If True, only the value of the parameters that are free will
             be
            exported.

        Notes
        -----
        The name of the files will be determined by each the Component
        and
        each Parameter name attributes. Therefore, it is possible to
        customise
        the file names modify the name attributes.

        """
        if only_free:
            parameters = self.free_parameters
        else:
            parameters = self.parameters

        parameters = [k for k in parameters if k.twin is None]
        for parameter in parameters:
            parameter.export(
                folder=folder,
                format=format,
                save_std=save_std,
            )

    def summary(self):
        for parameter in self.parameters:
            dim = len(parameter.map.squeeze().shape) if parameter.map \
                is not None else 0
            if parameter.twin is None:
                if dim <= 1:
                    print('%s = %s ± %s %s' % (parameter.name, parameter.value,
                                               parameter.std, parameter.units))

    def __call__(self):
        """Returns the corresponding model for the current coordinates

        Returns
        -------
        numpy array
        """
        axis = self.model.axis.axis[self.model.channel_switches]
        component_array = self.function(axis)
        return component_array

    def _component2plot(self, axes_manager, out_of_range2nans=True):
        old_axes_manager = None
        if axes_manager is not self.model.axes_manager:
            old_axes_manager = self.model.axes_manager
            self.model.axes_manager = axes_manager
            self.fetch_stored_values()
        s = self.model.__call__(component_list=[self])
        if not self.active:
            s.fill(np.nan)
        if old_axes_manager is not None:
            self.model.axes_manager = old_axes_manager
            self.charge()
        if out_of_range2nans is True:
            ns = np.empty(self.model.axis.axis.shape)
            ns.fill(np.nan)
            ns[self.model.channel_switches] = s
            s = ns
        if old_axes_manager is not None:
            self.model.axes_manager = old_axes_manager
            self.fetch_stored_values()
        return s

    def set_parameters_free(self, parameter_name_list=None):
        """
        Sets parameters in a component to free.

        Parameters
        ----------
        parameter_name_list : None or list of strings, optional
            If None, will set all the parameters to free.
            If list of strings, will set all the parameters with the same name
            as the strings in parameter_name_list to free.

        Examples
        --------
        >>> v1 = hs.model.components1D.Voigt()
        >>> v1.set_parameters_free()
        >>> v1.set_parameters_free(parameter_name_list=['area','centre'])

        See also
        --------
        set_parameters_not_free
        hyperspy.model.BaseModel.set_parameters_free
        hyperspy.model.BaseModel.set_parameters_not_free
        """

        parameter_list = []
        if not parameter_name_list:
            parameter_list = self.parameters
        else:
            for _parameter in self.parameters:
                if _parameter.name in parameter_name_list:
                    parameter_list.append(_parameter)

        for _parameter in parameter_list:
            _parameter.free = True

    def set_parameters_not_free(self, parameter_name_list=None):
        """
        Sets parameters in a component to not free.

        Parameters
        ----------
        parameter_name_list : None or list of strings, optional
            If None, will set all the parameters to not free.
            If list of strings, will set all the parameters with the same name
            as the strings in parameter_name_list to not free.

        Examples
        --------
        >>> v1 = hs.model.components1D.Voigt()
        >>> v1.set_parameters_not_free()
        >>> v1.set_parameters_not_free(parameter_name_list=['area','centre'])

        See also
        --------
        set_parameters_free
        hyperspy.model.BaseModel.set_parameters_free
        hyperspy.model.BaseModel.set_parameters_not_free
        """

        parameter_list = []
        if not parameter_name_list:
            parameter_list = self.parameters
        else:
            for _parameter in self.parameters:
                if _parameter.name in parameter_name_list:
                    parameter_list.append(_parameter)

        for _parameter in parameter_list:
            _parameter.free = False

    def _estimate_parameters(self, signal):
        self.binned = signal.metadata.Signal.binned
        if self._axes_manager != signal.axes_manager:
            self._axes_manager = signal.axes_manager
            self._create_arrays()

    def as_dictionary(self, fullcopy=True):
        """Returns component as a dictionary
        For more information on method and conventions, see
        :meth:`hyperspy.misc.export_dictionary.export_to_dictionary`

        Parameters
        ----------
        fullcopy : Bool (optional, False)
            Copies of objects are stored, not references. If any found,
            functions will be pickled and signals converted to dictionaries

        Returns
        -------
        dic : dictionary
            A dictionary, containing at least the following fields:
            parameters : list
                a list of dictionaries of the parameters, one per
            _whitelist : dictionary
                a dictionary with keys used as references saved attributes, for
                more information, see
                :meth:`hyperspy.misc.export_dictionary.export_to_dictionary`
            * any field from _whitelist.keys() *
        """
        dic = {
            'parameters': [p.as_dictionary(fullcopy) for p in self.parameters]
        }
        dic.update(get_object_package_info(self))
        export_to_dictionary(self, self._whitelist, dic, fullcopy)
        from hyperspy.model import _COMPONENTS
        if self._id_name not in _COMPONENTS:
            import dill
            dic['_class_dump'] = dill.dumps(self.__class__)
        return dic

    def _load_dictionary(self, dic):
        """Load data from dictionary.

        Parameters
        ----------
        dict : dictionary
            A dictionary containing following items:
            _id_name : string
                _id_name of the original component, used to create the
                dictionary. Has to match with the self._id_name
            parameters : list
                A list of dictionaries, one per parameter of the component (see
                parameter.as_dictionary() documentation for more)
            _whitelist : dictionary
                a dictionary, which keys are used as keywords to match with the
                component attributes.  For more information see
                :meth:`hyperspy.misc.export_dictionary.load_from_dictionary`
            * any field from _whitelist.keys() *

        Returns
        -------
        twin_dict : dictionary
            Dictionary of 'id' values from input dictionary as keys with all of
            the parameters of the component, to be later used for setting up
            correct twins.
        """

        if dic['_id_name'] == self._id_name:
            if (self._id_name == "Polynomial" and
                    LooseVersion(hyperspy.__version__) >= LooseVersion("2.0")):
                # in HyperSpy 2.0 the polynomial definition changed
                from hyperspy._components.polynomial import convert_to_polynomial
                dic = convert_to_polynomial(dic)
            load_from_dictionary(self, dic)
            id_dict = {}
            for p in dic['parameters']:
                idname = p['_id_name']
                if hasattr(self, idname):
                    par = getattr(self, idname)
                    t_id = par._load_dictionary(p)
                    id_dict[t_id] = par
                else:
                    raise ValueError(
                        "_id_name of parameters in component and dictionary do not match"
                    )
            return id_dict
        else:
            raise ValueError(
                "_id_name of component and dictionary do not match, \ncomponent._id_name = %s\
                    \ndictionary['_id_name'] = %s" %
                (self._id_name, dic['_id_name']))

    def print_current_values(self, only_free=False, fancy=True):
        """Prints the current values of the component's parameters.
        Parameters
        ----------
        only_free : bool
            If True, only free parameters will be printed.
        fancy : bool
            If True, attempts to print using html rather than text in the notebook.
        """
        if fancy:
            display(current_component_values(self, only_free=only_free))
        else:
            display_pretty(current_component_values(self, only_free=only_free))
class Slide34Expr(bu.SymbExpr):

    # control and state variables
    w, s_x, s_y, Eps, Sig = w, s_x, s_y, Eps, Sig

    # model parameters
    E_T = E_T
    gamma_T = gamma_T
    K_T = K_T
    S_T = S_T
    c_T = c_T
    bartau = bartau
    E_N = E_N
    S_N = S_N
    c_N = c_N
    f_t = f_t
    f_c = f_c
    f_c0 = f_c0
    m = m
    eta = eta
    r = r
    H_switch = H_switch
    Sig_signs = Sig_signs
    c_NT = c_NT
    S_NT = S_NT

    ONE = Cymbol(r'I')
    ZERO = Cymbol(r'O')

    # expressions
    Sig_ = Sig_.T
    dSig_dEps_ = dSig_dEps_.subs(0, ZERO) * ONE
    f_ = f_
    df_dSig_ = df_dSig_.subs(0, ZERO) * ONE
    ddf_dEps_ = ddf_dEps_.subs(0, ZERO) * ONE

    phi_final_ = tr.Property()

    @tr.cached_property
    def _get_phi_final_(self):
        def g_ari_integrity(var1, var2):
            return 1 - sp.sqrt((1 - var1) * (1 - var2))

        omega_NT = g_ari_integrity(omega_N, omega_T)
        phi_N = (1 - omega_N)**(c_N) * S_N / (r + 1) * (Y_N / S_N)**(
            r + 1) * H_switch
        phi_T = (1 - omega_T)**(c_T) * S_T / (r + 1) * (Y_T / S_T)**(r + 1)
        phi_NT = (1 - omega_NT)**(c_NT) * S_NT / (r + 1) * (
            (Y_N + Y_T) / S_NT)**(r + 1)
        phi_ = f_ + ((1 - eta) *
                     (phi_N + phi_T) + eta * phi_NT) * (bartau /
                                                        (bartau - m * sig_pi))
        return phi_.subs(
            r, 1
        )  # @TODO - fix the passing of the parameter - it damages the T response

    Phi_final_ = tr.Property()

    @tr.cached_property
    def _get_Phi_final_(self):
        return -self.Sig_signs * self.phi_final_.diff(self.Sig)

    H_sig_pi_ = H(sig_pi)

    sig_eff_ = sig_pi / (1 - H(sig_pi) * omega_N)
    tau_eff_x_ = tau_pi_x / (1 - omega_T)
    tau_eff_y_ = tau_pi_y / (1 - omega_T)

    symb_model_params = [
        'E_T', 'gamma_T', 'K_T', 'S_T', 'c_T', 'bartau', 'E_N', 'S_N', 'c_N',
        'm', 'f_t', 'f_c', 'f_c0', 'eta', 'r', 'c_NT', 'S_NT'
    ]

    # List of expressions for which the methods `get_`
    symb_expressions = [
        ('Sig_', ('w', 's_x', 's_y', 'Sig', 'Eps')),
        ('dSig_dEps_', ('w', 's_x', 's_y', 'Sig', 'Eps', 'ZERO', 'ONE')),
        ('f_', ('Eps', 'Sig', 'H_switch')),
        ('df_dSig_', ('Eps', 'Sig', 'H_switch', 'ZERO', 'ONE')),
        ('ddf_dEps_', ('Eps', 'Sig', 'H_switch', 'ZERO', 'ONE')),
        ('phi_final_', ('Eps', 'Sig', 'H_switch')),
        ('Phi_final_', ('Eps', 'Sig', 'H_switch', 'ZERO', 'ONE')),
        ('H_sig_pi_', ('Sig', ))
    ]