"""Output file: Homogenized strain/stress results.
This module includes the class associated with the output file where the
homogenized strain/stress results are stored.
Classes
-------
HomResOutput
    Output file: Homogenized strain/stress results.
"""
#
#                                                                       Modules
# =============================================================================
# Standard
import copy
# Third-party
import numpy as np
# Local
import tensor.matrixoperations as mop
import tensor.tensoroperations as top
from ioput.incoutputfiles.interface import IncrementalOutputFile
from material.materialoperations import compute_spatial_log_strain, \
                                        cauchy_from_first_piola, \
                                        MaterialQuantitiesComputer
#
#                                                          Authorship & Credits
# =============================================================================
__author__ = 'Bernardo Ferreira (bernardo_ferreira@brown.edu)'
__credits__ = ['Bernardo Ferreira', ]
__status__ = 'Stable'
# =============================================================================
#
# =============================================================================
[docs]class HomResOutput(IncrementalOutputFile):
    """Output file: Homogenized strain/stress results.
    Attributes
    ----------
    _file_path : str
        Output file path.
    _header : list[str]
        List containing the header of each column (str).
    _col_width : int
        Output file column width.
    Methods
    -------
    init_file(self, strain_formulation)
        Open output file and write file header.
    write_file(self, strain_formulation, problem_type, mac_load_path, \
               hom_results, effective_time)
        Write output file.
    """
[docs]    def __init__(self, file_path):
        """Constructor.
        Parameters
        ----------
        file_path : str
            Output file path.
        """
        self._file_path = file_path
        # Set output file header
        self._header = ['Increment', 'RunEffectTime', 'LoadSubpath',
                        'LoadFactor', 'Time', 'SubincLevel',
                        'strain_11', 'strain_21', 'strain_31',
                        'strain_12', 'strain_22', 'strain_32',
                        'strain_13', 'strain_23', 'strain_33',
                        'stress_11', 'stress_21', 'stress_31',
                        'stress_12', 'stress_22', 'stress_32',
                        'stress_13', 'stress_23', 'stress_33',
                        'vm_strain', 'vm_stress']
        # Set column width
        self._col_width = max(16, max([len(x) for x in self._header]) + 2) 
    # -------------------------------------------------------------------------
[docs]    def init_file(self, strain_formulation):
        """Open output file and write file header.
        Parameters
        ----------
        strain_formulation: {'infinitesimal', 'finite'}
            Problem strain formulation.
        """
        # Set loading initial output
        load_init = (0, 0.0, 0, 0.0, 0.0, 0)
        # Set strain and stress initial output
        strain_init = 9*[0.0, ]
        stress_init = 9*[0.0, ]
        if strain_formulation == 'finite':
            strain_init[0] = 1.0
            strain_init[4] = 1.0
            strain_init[8] = 1.0
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        # Open output file (write mode)
        output_file = open(self._file_path, 'w')
        # Set output file header format structure
        write_list = [
            '{:>9s}'.format(self._header[0])
            + ''.join([('{:>' + str(self._col_width) + 's}').format(x)
                       for x in self._header[1:]]),
            '\n' + '{:>9d}'.format(load_init[0])
            + ('{:>' + str(self._col_width) + '.8e}').format(load_init[1])
            + ('{:>' + str(self._col_width) + 'd}').format(load_init[2])
            + ''.join([('{:>' + str(self._col_width) + '.8e}').format(x)
                       for x in load_init[3:5]])
            + ('{:>' + str(self._col_width) + 'd}').format(load_init[5])
            + ''.join([('{:>' + str(self._col_width) + '.8e}').format(x)
                       for x in strain_init])
            + ''.join([('{:>' + str(self._col_width) + '.8e}').format(x)
                       for x in stress_init])
            + ''.join([('{:>' + str(self._col_width) + '.8e}').format(0.0)
                       for x in range(2)])]
        # Write output file header
        output_file.writelines(write_list)
        # Close output file
        output_file.close() 
    # -------------------------------------------------------------------------
[docs]    def write_file(self, strain_formulation, problem_type, mac_load_path,
                   hom_results, effective_time):
        """Write output file.
        Parameters
        ----------
        strain_formulation: {'infinitesimal', 'finite'}
            Problem strain formulation.
        problem_type : int
            Problem type: 2D plane strain (1), 2D plane stress (2),
            2D axisymmetric (3) and 3D (4).
        mac_load_path : LoadingPath
            Macroscale loading path.
        hom_results : dict
            Homogenized strain/stress results stored as:
            * 'strain' : homogenized strain tensor (numpy.ndarray (2d))
            * 'stress' : homogenized stress tensor (numpy.ndarray (2d))
            * 'hom_stress_33' : homogenized out-of-plain stress (float)
            Infinitesimal strain tensor and Cauchy stress tensor (infinitesimal
            strains) or Deformation gradient and first Piola-Kirchhoff stress
            tensor (finite strains).
        effective_time : float
            Current time (s) associated with the solution of the equilibrium
            problem.
        """
        # Get loading path data
        sp_id, sp_inc, sp_total_lfact, _, sp_total_time, _, subinc_level = \
            
mac_load_path.get_subpath_state()
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        # Get homogenized strain/stress data
        hom_strain = hom_results['hom_strain']
        hom_stress = hom_results['hom_stress']
        if problem_type == 1:
            hom_stress_33 = hom_results['hom_stress_33']
        # When the problem type corresponds to a 2D analysis, build the 3D
        # homogenized strain and stress tensors by considering the appropriate
        # out-of-plane strain and stress components
        out_hom_strain = np.zeros((3, 3))
        out_hom_stress = np.zeros((3, 3))
        if problem_type == 1:
            out_hom_strain[0:2, 0:2] = hom_strain
            if strain_formulation == 'finite':
                out_hom_strain[2, 2] = 1.0
            out_hom_stress[0:2, 0:2] = hom_stress
            out_hom_stress[2, 2] = hom_stress_33
        else:
            out_hom_strain[:, :] = hom_strain
            out_hom_stress[:, :] = hom_stress
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        # Get 3D problem parameters
        n_dim, comp_order_sym, _ = \
            
mop.get_problem_type_parameters(problem_type=4)
        # Get fourth-order tensors
        _, _, _, _, _, _, fodevprojsym = top.get_id_operators(n_dim)
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        # Instantiate material state computations
        csbvar_computer = MaterialQuantitiesComputer()
        # Compute spatial logarithmic strain tensor and Cauchy stress tensor
        if strain_formulation == 'infinitesimal':
            strain = copy.deepcopy(out_hom_strain)
            cauchy_stress = copy.deepcopy(out_hom_stress)
        else:
            # Compute spatial logarithmic strain tensor
            strain = compute_spatial_log_strain(out_hom_strain)
            # Get Cauchy stress tensor from first Piola-Kirchhoff stress tensor
            cauchy_stress = cauchy_from_first_piola(out_hom_strain,
                                                    out_hom_stress)
        # Get spatial logarithmic strain tensor (matricial form)
        strain_mf = mop.get_tensor_mf(strain, n_dim, comp_order_sym)
        # Get Cauchy stress tensor (matricial form)
        cauchy_stress_mf = mop.get_tensor_mf(cauchy_stress, n_dim,
                                             comp_order_sym)
        # Compute von Mises equivalent strain
        vm_strain = csbvar_computer.get_vm_strain(strain_mf)
        # Compute von Mises equivalent stress
        vm_stress = csbvar_computer.get_vm_stress(cauchy_stress_mf)
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        # Open homogenized results output file (append mode)
        output_file = open(self._file_path, 'a')
        # Set increment homogenized results format structure
        inc_data = [sp_inc, effective_time, sp_id, sp_total_lfact,
                    sp_total_time, subinc_level,
                    out_hom_strain[0, 0], out_hom_strain[1, 0],
                    out_hom_strain[2, 0], out_hom_strain[0, 1],
                    out_hom_strain[1, 1], out_hom_strain[2, 1],
                    out_hom_strain[0, 2], out_hom_strain[1, 2],
                    out_hom_strain[2, 2], out_hom_stress[0, 0],
                    out_hom_stress[1, 0], out_hom_stress[2, 0],
                    out_hom_stress[0, 1], out_hom_stress[1, 1],
                    out_hom_stress[2, 1], out_hom_stress[0, 2],
                    out_hom_stress[1, 2], out_hom_stress[2, 2],
                    vm_strain, vm_stress]
        write_list = \
            
['\n' + ('{:>9d}').format(inc_data[0])
             + ('{:>' + str(self._col_width) + '.8e}').format(inc_data[1])
             + ('{:>' + str(self._col_width) + 'd}').format(inc_data[2])
             + ''.join([('{:>' + str(self._col_width) + '.8e}').format(x)
                        for x in inc_data[3:5]])
             + ('{:>' + str(self._col_width) + 'd}').format(inc_data[5])
             + ''.join([('{:>' + str(self._col_width) + '.8e}').format(x)
                        for x in inc_data[6:]])]
        # Write increment homogenized results
        output_file.writelines(write_list)
        # Close output file
        output_file.close()