#=============================================================================
#
#  Copyright (c) Kitware, Inc.
#  All rights reserved.
#  See LICENSE.txt for details.
#
#  This software is distributed WITHOUT ANY WARRANTY; without even
#  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
#  PURPOSE.  See the above copyright notice for more information.
#
#=============================================================================
import os
print 'loading', os.path.basename(__file__)

import smtk
if 'pybind11' == smtk.wrappingProtocol():
  #print 'Using pybind11 bindings'
  import smtk.attribute
  import smtk.model
from cardformat import CardFormat

# ---------------------------------------------------------------------
# Strings used in "if_condition" arguments (for convenience only)
NAMELIST = 'namelist'
THERMAL_ANALYSIS = 'thermal-analysis'
ONLY_THERMAL_ANALYSIS = 'only-thermal-analysis'
FLOW_ANALYSIS = 'flow-analysis'
INVISCID_FLOW = 'inviscid_flow'
VISCOUS_FLOW = 'viscous-flow'
FLUID_PHASE = 'fluid-phase'
SOLID_PHASE = 'solid-phase'
MASS_LIMITER = 'mass-limiter'
BC_INFLOW = 'bc-inflow'
VOID_MATERIAL = 'void'  # (must match material/material-type item)
ENCLOSURE_RADIATION = 'enclosure-radiation'
MOVING_RADIATION = 'moving-radiation'

# ---------------------------------------------------------------------
class Writer:
  '''Top level writer class for Truchas input files.
  '''

# ---------------------------------------------------------------------
  def __init__(self, export_spec):
    '''
    '''
    self.sim_atts = export_spec.getSimulationAttributes()
    print 'sim_atts', self.sim_atts
    self.model_manager = self.sim_atts.refModelManager()

    self.background_material_id = 0
    self.format_table = dict()
    self.interface_set_ids = list()
    self.enclosure_surface_set_ids = list()
    self.moving_enclosure_surface_set_ids = list()
    # key = material att id; value = output material_number
    self.material_number_dict = dict()
    self.mesh_file = 'NOT-FOUND'
    self.namelist_sequence = list()
    self.out = None

# ---------------------------------------------------------------------
  def write(self, path, namelist_sequence, format_table):
    '''
    '''
    self.namelist_sequence = namelist_sequence
    self.format_table = format_table

    self._setup()

    completed = False
    with open(path, 'w') as out:
      out.write('Generated by CMB\n')
      self.out = out

      for namelist in self.namelist_sequence:
        format_list = self.format_table.get(namelist.title)

        # Check conditions
        if not CardFormat.test_conditions(namelist.if_condition):
          continue

        # Namelists can assign custom method
        if namelist.custom_method is not None:
          if not hasattr(self, namelist.custom_method):
            print 'ERROR: For namelist', namelist.title, \
              ', custom_method', namelist.custom_method, \
              'not found'
          else:
            method = getattr(self, namelist.custom_method)
            method(namelist, format_list)
          continue

        # Else use the default namelist writer
        else:
          self._write_namelist_default(namelist, format_list)

      completed = True
      print 'Wrote project file %s' % path
    return completed

# ---------------------------------------------------------------------
  def _write_namelist_default(self, namelist, format_list):
    '''
    '''
    print 'Writing', namelist.title, 'namelists'

    # If namelist specifies attribute, process each one
    if namelist.att_type is not None:
      att_list = self.sim_atts.findAttributes(namelist.att_type)
      att_list.sort(key=lambda att: att.name())
      for att in att_list:
        self._write_default_att(att, namelist, format_list)
      return

    # Otherwise process each card in format_list
    else:
      self.out.write('\n&%s\n' % namelist.title)
      for card in format_list:
        att_list = self.sim_atts.findAttributes(card.att_type)
        for att in att_list:
          card.write(self.out, att, base_item_path=namelist.base_item_path)
      self.out.write('/\n')

# ---------------------------------------------------------------------
  def _write_default_att(self, att, namelist, format_list):
    '''Writes namelist for 1 attribute
    '''
    self.out.write('\n&%s\n' % namelist.title)

    for card in format_list:
      if card.att_type is None:
        card.write(
          self.out, att, base_item_path=namelist.base_item_path)
      else:
        card_att_list = self.sim_atts.findAttributes(card.att_type)
        for card_att in card_att_list:
          card.write(self.out, card_att)
    self.out.write('/\n')

# ---------------------------------------------------------------------
  def _write_mesh(self, namelist, format_list):
    '''
    '''
    print 'Writing', namelist.title
    self.out.write('\n&%s\n' % namelist.title)

    att_list = self.sim_atts.findAttributes(namelist.att_type)
    mesh_att = att_list[0]
    for card in format_list:
      if not card.is_custom:
        card.write(self.out, mesh_att)
      elif 'mesh_file' == card.keyword:
        CardFormat.write_value(self.out, card.keyword, self.mesh_file)
      elif 'interface_side_sets' == card.keyword:
        if self.interface_set_ids:
          side_set_string = ','.join([str(x) for x in self.interface_set_ids])
          CardFormat.write_value(
            self.out, 'interface_side_sets', side_set_string, quote_string=False)

    self.out.write('/\n')

# ---------------------------------------------------------------------
  def _write_enclosure(self, namelist, format_list):
    '''
    '''
    print 'Writing', namelist.title
    self.out.write('\n&%s\n' % namelist.title)

    att_list = self.sim_atts.findAttributes(namelist.att_type)
    enclosure_att = att_list[0]
    for card in format_list:
      if not card.is_custom:
        if card.att_type is None:
          card.write(self.out, enclosure_att, base_item_path=namelist.base_item_path)
        else:
            card_att_list = self.sim_atts.findAttributes(card.att_type)
            card_att = card_att_list[0]
            card.write(self.out, card_att)
      elif 'mesh_file' == card.keyword:
        CardFormat.write_value(self.out, card.keyword, self.mesh_file)
      elif 'side_set_ids' == card.keyword:
        side_set_string = ', '.join([str(x) for x in self.enclosure_surface_set_ids])
        CardFormat.write_value(
          self.out, card.keyword, side_set_string, quote_string=False)
      elif 'displacement_set_ids' == card.keyword:
        side_set_string = ', '.join([str(x) for x in self.moving_enclosure_surface_set_ids])
        CardFormat.write_value(
          self.out, card.keyword, side_set_string, quote_string=False)
      elif 'symmetries' == card.keyword:
        full_path = namelist.base_item_path + '/' + card.item_path
        item = enclosure_att.itemAtPath(full_path, '/')
        group_item = smtk.attribute.to_concrete(item)
        number_groups = group_item.numberOfGroups()
        if number_groups < 1:
          continue

        symmetry_list = list()
        for i in range(number_groups):
          child_item = group_item.item(i, 0)
          string_item = smtk.attribute.to_concrete(child_item)
          symmetry_value = string_item.value(0)
          if symmetry_value.startswith('Rot'):
            item = string_item.activeChildItem(0)
            folds_item = smtk.attribute.to_concrete(item)
            folds_value = folds_item.value(0)
            symmetry_value = '%s%d' % (symmetry_value, folds_value)
          symmetry_list.append(symmetry_value)
        combined_value = ", ".join(symmetry_list)
        CardFormat.write_value(
          self.out, card.keyword, combined_value, quote_string=False)

      elif 'displacement_sequence' == card.keyword:
        full_path = namelist.base_item_path + '/' + card.item_path
        item = enclosure_att.itemAtPath(full_path, '/')
        group_item = smtk.attribute.to_concrete(item)
        CardFormat.write_value(
          self.out, card.keyword, '', quote_string=False, tab=13)

        number_groups = group_item.numberOfGroups()
        last_index = number_groups - 1
        for i in range(number_groups):
          child_item = group_item.item(i, 0)
          double_item = smtk.attribute.to_concrete(child_item)
          x = double_item.value(1)
          y = double_item.value(2)
          z = double_item.value(3)
          line = '    %f, %f, %f' % (x, y, z)
          if i != last_index:
            line += ','
          line += '\n'
          self.out.write(line)
      else:
        print 'WARNING skipping custom card for', card.keyword

    self.out.write('/\n')

# ---------------------------------------------------------------------
  def _write_enclosure_radiation(self, namelist, format_list):
    '''
    '''
    print 'Writing', namelist.title
    self.out.write('\n&%s\n' % namelist.title)

    att_list = self.sim_atts.findAttributes(namelist.att_type)
    enclosure_att = att_list[0]
    for card in format_list:
      if not card.is_custom:
        card.write(self.out, enclosure_att, base_item_path=namelist.base_item_path)
      elif 'time_sequence' == card.keyword:
        full_path = namelist.base_item_path + '/' + card.item_path
        item = enclosure_att.itemAtPath(full_path, '/')
        group_item = smtk.attribute.to_concrete(item)
        CardFormat.write_value(
          self.out, card.keyword, '', quote_string=False, tab=13)

        number_groups = group_item.numberOfGroups()
        last_index = number_groups - 1
        for i in range(number_groups):
          child_item = group_item.item(i, 0)
          double_item = smtk.attribute.to_concrete(child_item)
          time_value = double_item.value(0)
          line = '    %f' % time_value
          if i != last_index:
            line += ','
          line += '\n'
          self.out.write(line)
      else:
        print 'WARNING skipping custom card for', card.keyword

    self.out.write('/\n')

# ---------------------------------------------------------------------
  def _write_thermal_condition(self, namelist, format_list):
    '''Common writer for ds_boundary_condition, ds_interface_condition, enclosure-surface

    The logic is hairy for several reasons:
      * Template uses discrete "type" item instead of attribute sub-types
      * The ds-boundary conditions combine "htc" and "radiation" in a
        single definition, but the writer needs to generate separate
        namelist for them.
      * Some conditions have a single data value, and some have a group
        comprising multiple values.
    '''
    # Use table to associate namelist with "type" item
    condition_type_dict = {
      'DS_BOUNDARY_CONDITION': 'ds-boundary-condition',
      'DS_INTERFACE_CONDITION': 'ds-interface-condition',
      'ENCLOSURE_SURFACE': 'enclosure-surface'
    }
    condition_type = condition_type_dict.get(namelist.title)
    print 'Writing %s namelists (%s)' % (namelist.title, condition_type)

    att_list = self.sim_atts.findAttributes(namelist.att_type)
    att_list.sort(key=lambda att: att.name())
    for att in att_list:
      model_ent_item = att.associations()
      if model_ent_item is None or (0 == model_ent_item.numberOfValues()):
        print 'Skipping attribute type \"%s\", name \"%s\" -- no associations' % \
          (att.type(), att.name())
        continue

      type_item = att.findString('type')
      if type_item.value(0) != condition_type:
        continue

      # Logic for enclosure_surface is straightforward
      if 'enclosure-surface' == condition_type:
        print 'Call write_default'
        self._write_default_att(att, namelist, format_list)
        continue

      # Common logic for ds-boundary-condition and ds-interface-condition
      item = att.itemAtPath(namelist.base_item_path, '/')
      #print '  item', item, item.name()
      condition_item = smtk.attribute.to_concrete(item)
      condition_value = condition_item.value(0)
      num_conditions = condition_item.numberOfActiveChildrenItems()
      for i in range(num_conditions):
        active_child = condition_item.activeChildItem(i)
        if not active_child.isEnabled():
          continue

        active_item = smtk.attribute.to_concrete(active_child)
        output_name = att.name()
        if num_conditions > 1:
          # For htc-plus-radition, override the condition value
          condition_value = active_item.name()
          # And create separate BC names
          output_name += '::%s' % condition_value

        self.out.write('\n&%s\n' % namelist.title)
        CardFormat.write_value(self.out, 'name', output_name)

        # Write the condition card
        CardFormat.write_value(self.out, 'condition', condition_value)

        # Write face set ids
        face_set_string = CardFormat.get_model_entity_ids(
          model_ent_item, as_string=True)
        CardFormat.write_value(
          self.out, 'face_set_ids', face_set_string, quote_string=False)

        if active_item.type() == smtk.attribute.Item.DoubleType:
          CardFormat.write_value(self.out, 'data_constant', active_item.value(0))
        if active_item.type() == smtk.attribute.Item.GroupType:
          value_list = list()
          for i in range(active_item.numberOfItemsPerGroup()):
            subitem = active_item.item(0, i)
            value_item = smtk.attribute.to_concrete(subitem)
            value_list.append(value_item.value(0))

          string_list = [str(x) for x in value_list]
          string_value = ', '.join(string_list)
          CardFormat.write_value(
            self.out, 'data_constant', string_value, quote_string=False)

        self.out.write('/\n')

# ---------------------------------------------------------------------
  def _write_bc(self, namelist, format_list):
    '''Common writer for fluid boundary conditions

    '''
    print 'Writing', namelist.title, 'namelists'
    att_list = self.sim_atts.findAttributes(namelist.att_type)
    att_list.sort(key=lambda att: att.name())
    for att in att_list:
      model_ent_item = att.associations()
      if model_ent_item is None or (0 == model_ent_item.numberOfValues()):
        print 'Skipping attribute type \"%s\", name \"%s\" -- no associations' % \
          (att.type(), att.name())
        continue

      bc_value = None
      bc_expression = None
      # Get bc_type
      bc_type = 'dirichlet'  # default
      var_item = att.findString('variable')
      if ('velocity' == var_item.value(0)):
        #item = var_item.findChild('velocity-bc-type', smtk.attribute.SearchStyle.ACTIVE_CHILDREN)
        item = att.itemAtPath('variable/velocity-bc-type', '/')
        bc_type_item = smtk.attribute.to_concrete(item)
        bc_type = bc_type_item.value(0)

        if bc_type:
          item = att.itemAtPath(
            'variable/velocity-bc-type/velocity-group/velocity-value', '/')
          bc_value_item = smtk.attribute.to_concrete(item)
          value_list = [
            bc_value_item.value(1),
            bc_value_item.value(2),
            bc_value_item.value(3)]
          string_list = [str(x) for x in value_list]
          bc_value = ', '.join(string_list)
      else:
        item = att.itemAtPath('variable/pressure-value', '/')
        bc_value_item = smtk.attribute.to_concrete(item)
        if bc_value_item.isExpression(0):
          expression_att = bc_value_item.expression(0)
          bc_expression = expression_att.name()
        else:
          bc_value = bc_value_item.value(0)

      # Get enabled state for inflow group
      inflow_item = att.itemAtPath('variable/inflow', '/')
      if inflow_item.isEnabled():
        CardFormat.Conditions.add(BC_INFLOW)

      self.out.write('\n&%s\n' % namelist.title)
      for card in format_list:
        if 'bc_type' == card.keyword:
          CardFormat.write_value(self.out, card.keyword, bc_type)
        elif 'bc_value' == card.keyword:
          if bc_expression is not None:
            CardFormat.write_value(
              self.out, card.expression_keyword, bc_expression)
          elif bc_value is not None:
            CardFormat.write_value(
              self.out, card.keyword, bc_value, quote_string=False)
        else:
          card.write(
            self.out, att, base_item_path=namelist.base_item_path)
      self.out.write('/\n')
      CardFormat.Conditions.discard(BC_INFLOW)

# ---------------------------------------------------------------------
  def _write_body(self, namelist, format_list):
    '''Common writer for BODY namelists

    '''
    print 'Writing', namelist.title, 'namelists'
    att_list = self.sim_atts.findAttributes(namelist.att_type)
    att_list.sort(key=lambda att: att.name())
    for att in att_list:
      model_ent_item = att.associations()
      if model_ent_item is None or (0 == model_ent_item.numberOfValues()):
        print 'Skipping attribute type \"%s\", name \"%s\" -- no associations' % \
          (att.type(), att.name())
        continue

      # Write separate BODY namelist for each model item
      for i in range(model_ent_item.numberOfValues()):
        model_ent_ref = model_ent_item.value(i)

        self.out.write('\n&%s\n' % namelist.title)
        for card in format_list:
          if 'material_number' == card.keyword:
            # In _setup(), info was attached to each model entity
            model_ent = model_ent_ref.entity()
            idlist = self.model_manager.integerProperty(
              model_ent, 'material number')
            if not idlist:
              continue

            # Set material number
            material_number = idlist[0]

            # But for two-phase materials, use fluid material number,
            # per email discussions 23-Jun-2016.
            # And for two-phase materials, the fluid number is +1 more
            # than the property set on the model entity.
            att_uuid_list = model_ent_ref.attributes()
            #print 'att_uuid_list', att_uuid_list, len(att_uuid_list)
            for att_uuid in att_uuid_list:
              sibling_att = self.sim_atts.findAttribute(att_uuid)
              #print '  sibling type', sibling_att.type()
              if sibling_att.type() == 'material':
                mat_type_item = sibling_att.findString('material-type')
                mat_type = mat_type_item.value(0)
                #print 'material type:', mat_type
                if 'two-phase' == mat_type:
                  material_number += 1
                break

            CardFormat.write_value(self.out, card.keyword, material_number)
          else:
            card.write(self.out, att)
        self.out.write('/\n')

# ---------------------------------------------------------------------
  def _write_outputs(self, namelist, format_list):
    '''Writes OUTPUTS namelist

    Needs custom logic because of item organization
      attribute type "outputs"
        double item "start-time"
        double item "end-time"
        double item  "output-dt"
        optional extensible group item "output-times"
          double item "time"
            component 0 = output_t
            component 1 = output_dt

    '''
    print 'Writing', namelist.title
    att_list = self.sim_atts.findAttributes(namelist.att_type)
    time_list = list()
    dt_list = list()
    try:
      att = att_list[0]
      start_time = att.findDouble('start-time').value(0)
      end_time = att.findDouble('end-time').value(0)
      dt = att.findDouble('output-dt').value(0)

      time_list.append(start_time)
      dt_list.append(dt)

      # Check for optional output-times
      item = att.itemAtPath('output-times', '/')
      group_item = smtk.attribute.to_concrete(item)
      num_groups = group_item.numberOfGroups()
      for i in range(num_groups):
        item = group_item.item(i, 0)
        double_item = smtk.attribute.to_concrete(item)
        time_list.append(double_item.value(0))
        dt_list.append(double_item.value(1))
      time_list.append(end_time)
    except Exception as ex:
      print 'ERROR ', ex
      return

    # Write each component as separate array
    self.out.write('\n&%s\n' % namelist.title)

    formatted_list = ['%s' % value for value in time_list]
    formatted_string = ', '.join(formatted_list)
    CardFormat.write_value(
      self.out, 'output_t', formatted_string, quote_string=False, tab=10)

    formatted_list = ['%s' % value for value in dt_list]
    formatted_string = ', '.join(formatted_list)
    CardFormat.write_value(
      self.out, 'output_dt', formatted_string, quote_string=False, tab=10)

    self.out.write('/\n')

# ---------------------------------------------------------------------
  def _write_simcontrol(self, namelist, format_list):
    '''Common writer for SIMULATION_CONTROL namelist

    '''
    print 'Writing', namelist.title, 'namelist'
    att_list = self.sim_atts.findAttributes(namelist.att_type)
    att = att_list[0]

    # Get group item, which is optional
    item = att.itemAtPath('simulation-control', '/')
    if not item.isEnabled():
      return

    # (else)
    self.out.write('\n&%s\n' % namelist.title)
    for card in format_list:
      card.write(self.out, att)
    self.out.write('/\n')

# ---------------------------------------------------------------------
  def _write_function(self, namelist, format_list):
    '''Common writer for FUNCTION namelists

    '''
    print 'Writing', namelist.title, 'namelists'
    att_list = self.sim_atts.findAttributes(namelist.att_type)
    att_list.sort(key=lambda att: att.name())
    for att in att_list:
      self.out.write('\n&%s\n' % namelist.title)
      #CardFormat.write_value(self.out, 'name', att.name())
      for card in format_list:
        card.write(self.out, att)
      # Write out the data
      CardFormat.write_value(self.out, 'table', '', quote_string=False)
      item = att.itemAtPath('ValuePairs/X', '/')
      x_item = smtk.attribute.to_concrete(item)
      item = att.itemAtPath('ValuePairs/Value', '/')
      y_item = smtk.attribute.to_concrete(item)
      num_rows = x_item.numberOfValues()
      last_row = num_rows - 1
      for i in range(num_rows):
        x = x_item.value(i)
        y = y_item.value(i)
        self.out.write('    %s, %s' % (x, y))
        if i < last_row:
          self.out.write(',')
        self.out.write('\n')
      self.out.write('/\n')

# ---------------------------------------------------------------------
  def _write_materials(self, namelist, format_list):
    '''Writes interleaved MATERIAL, PHASE, MATERIAL_SET namelists

    The basic sequence is:
      for each material:
        if material is solid or two-phase:
          write MATERIAL namelist
          write solid PHASE namelist
        if material is fluid or two-phase:
          write MATERIAL namelist
          write fluid PHASE namelist
        write MATERIAL_SYSTEM namelist
    '''
    print 'Writing MATERIAL/PHASE/MATERIAL_SYSTEM namelists'

    # Write materials (sort by material number)
    att_list = self.sim_atts.findAttributes('material')
    att_list.sort(key=lambda att: self.material_number_dict.get(att.id()))
    for att in att_list:
      persistent_id = att.id()
      print 'material attribute', persistent_id

      # Check that the material is used
      material_number = self.material_number_dict.get(persistent_id)
      if material_number is None:
        print 'WARNING: material %s is not used -- skipping' % att.name()
        continue

      material_type_item = att.findString('material-type')
      material_type = material_type_item.value(0)
      print 'material type:', material_type

      # Write MATERIAL namelist
      CardFormat.Conditions.add(material_type)
      material_format_list = self.format_table.get('MATERIAL')

      # For void materials, no PHASE or MATERIAL_SYSTEM namelist
      if "void" == material_type:
        self._write_material_namelist(
          att, att.name(), material_number, material_format_list)
        continue

      # Write PHASE namelist
      phase_format_list = self.format_table.get('PHASE')
      phase_names = list()
      if material_type in ['solid', 'two-phase']:
        solid_name = '%s - solid' % att.name()
        self._write_material_namelist(
          att, solid_name, material_number, material_format_list)
        self._write_phase_namelist(
          att, solid_name, SOLID_PHASE, phase_format_list)
        phase_names.append(solid_name)

      # Write MATERIAL_SYSTEM namelist
      if material_type in ['fluid', 'two-phase']:
        fluid_name = '%s - fluid' % att.name()
        # For two-phase materials use "second" id
        if material_type == 'two-phase':
          material_number += 1
        self._write_material_namelist(
          att, fluid_name, material_number, material_format_list)
        self._write_phase_namelist(
          att, fluid_name, FLUID_PHASE, phase_format_list)
        phase_names.append(fluid_name)

      self._write_material_system_namelist(att, phase_names)
      CardFormat.Conditions.remove(material_type)

# ---------------------------------------------------------------------
  def _write_material_namelist(self, att, name, number, format_list):
    '''
    '''
    namelist = 'MATERIAL'
    print 'Writing', namelist

    # Begin namelist
    self.out.write('\n&%s\n' % namelist)

    # Get assigned material number
    persistent_id = att.id()

    CardFormat.write_value(self.out, 'material_name', name)
    CardFormat.write_value(self.out, 'material_number', number)

    material_type = att.findString('material-type').value(0)
    immobile = ("solid" == material_type)
    CardFormat.write_value(self.out, 'immobile', immobile, as_boolean=True)

    # ?Write density
    for card in format_list:
      card.write(self.out, att)

    # Check if this material is the background material
    if persistent_id == self.background_material_id:
      CardFormat.write_value(self.out, 'material_feature','background')

    # End namelist
    self.out.write('/\n')

# ---------------------------------------------------------------------
  def _write_phase_namelist(self, att, phase_name, phase_condition, format_list):
    '''Write PHASE namelist for material attribute
    '''
    namelist = 'PHASE'
    print 'Writing', namelist, phase_condition
    self.out.write('\n&%s\n' % namelist)
    CardFormat.PropertyIndex = 0

    CardFormat.write_value(self.out, 'name', phase_name, tab=4)
    CardFormat.Conditions.add(phase_condition)

    for card in format_list:
      card.write(self.out, att)
    CardFormat.Conditions.remove(phase_condition)

    self.out.write('/\n')

# ---------------------------------------------------------------------
  def _write_material_system_namelist(self, att, phase_names):
    '''
    '''
    namelist = 'MATERIAL_SYSTEM'
    print 'Writing', namelist
    self.out.write('\n&%s\n' % namelist)

    if len(phase_names) == 1:
      tab = 8
      CardFormat.write_value(self.out, 'name', att.name(), tab=tab)
      CardFormat.write_value(self.out, 'phases', phase_names[0], tab=tab)
    else:
      # 2 phases
      CardFormat.write_value(self.out, 'name', att.name())
      phase_string = '\"%s\", \"%s\"' % tuple(phase_names)
      CardFormat.write_value(self.out, 'phases', phase_string, quote_string=False)

      # Write cards in format list
      format_list = self.format_table.get('MATERIAL_SYSTEM', [])
      for card in format_list:
        card.write(self.out, att)

    self.out.write('/\n')

# ---------------------------------------------------------------------
  def _setup(self):
    '''
    '''
    CardFormat.ModelManager = self.model_manager

    # Get mesh file from model manager
    mask = int(smtk.model.ModelEntityType)
    model_ents = self.model_manager.entitiesMatchingFlags(mask, True)
    #print 'model_ents', model_ents
    if not model_ents:
        msg = 'No model - export will be incomplete'
        print 'WARNING:', msg
    elif len(model_ents) > 1:
        msg = 'Multiple models - using first one'
        print 'WARNING:', msg
    model_ent = model_ents.pop()

    print 'has smtk_url?', self.model_manager.hasStringProperty(model_ent, 'smtk_url')

    urls = self.model_manager.stringProperty(model_ent, 'url')
    if urls:
        url = urls[0]
        print 'url', url

        # Get model fileanme
        model_file = os.path.basename(url)
        print 'model_file', model_file
        #self.output.write('  File: %s\n\n' % model_file)

        # Get full path to model
        model_path = model_file  # default case, when testing w/smtk
        if os.path.isabs(url):
          model_path = url
        else:
          smtk_urls = self.model_manager.stringProperty(model_ent, 'smtk_url')
          print 'smtk_urls', smtk_urls
          if smtk_urls:
              smtk_url = smtk_urls[0]
              model_path = os.path.join(smtk_url, url)
              model_path = os.path.abspath(model_path)
        print 'model_path', model_path
        self.mesh_file = model_path

    # Get analysis type and set CardFormat conditions
    att_list = self.sim_atts.findAttributes('solver')
    att_list.sort(key=lambda att: att.name())
    if att_list:
      solver_att = att_list[0]
      analysis_item = solver_att.findString('analysis')
      analysis = analysis_item.value(0)
      print 'analysis', analysis
      viscous_item = None
      if 'fluid' == analysis:
        CardFormat.Conditions.add(FLOW_ANALYSIS)
        viscous_item = solver_att.itemAtPath(
          'analysis/fluid/viscous-flow-model', '/')
      elif 'thermal' == analysis:
        CardFormat.Conditions.add(THERMAL_ANALYSIS)
        CardFormat.Conditions.add(ONLY_THERMAL_ANALYSIS)
      else:
        CardFormat.Conditions.add(FLOW_ANALYSIS)
        CardFormat.Conditions.add(THERMAL_ANALYSIS)
        viscous_item = solver_att.itemAtPath(
          'analysis/fluid/viscous-flow-model', '/')

      if viscous_item is not None:
        if viscous_item.isEnabled:
          CardFormat.Conditions.add(VISCOUS_FLOW)
        else:
          CardFormat.Conditions.add(INVISCID_FLOW)
      #print 'Initial Conditions', CardFormat.Conditions

    # Get the background material attribute's id
    att_list = self.sim_atts.findAttributes('background-material')
    if att_list:
      att = att_list[0]
      item = att.findRef('background-material')
      if item.isSet(0):
        self.background_material_id = item.value(0).id()

    # Assign material numbers
    material_count = 0
    att_list = self.sim_atts.findAttributes('material')
    att_list.sort(key=lambda att: att.name())
    for att in att_list:
      model_ent_item = att.associations()
      # Skip materials with no model associations
      # except for the background material
      if model_ent_item is None and att.id() != self.background_material_id:
        continue

      material_count += 1
      persistent_id = att.id()
      self.material_number_dict[persistent_id] = material_count

      # Attach material number to each model entity
      if model_ent_item is None:
        continue

      for i in range(model_ent_item.numberOfValues()):
        ent_ref = model_ent_item.value(i)
        ent = ent_ref.entity()
        self.model_manager.setIntegerProperty(
          ent, 'material number', material_count)

      # If material is two-phase, reserve TWO numbers, 1 for each phase
      material_type_item = att.findString('material-type')
      material_type = material_type_item.value(0)
      if material_type == 'two-phase':
        material_count += 1
        second_id = '%s/#/second' % persistent_id
        self.material_number_dict[second_id] = material_count

    print 'material count:', len(self.material_number_dict)

    # Find all interface_set_ids and enclosure_surface_set_ids
    self.interface_set_ids = list()
    self.enclosure_surface_set_ids = list()
    self.moving_enclosure_surface_set_ids = list()
    att_list = self.sim_atts.findAttributes('thermal-surface-condition')
    for att in att_list:
      model_ent_item = att.associations()
      if model_ent_item is None:
        continue

      type_item = att.findString('type')
      if type_item.value(0) == 'ds-interface-condition':
        self.interface_set_ids += CardFormat.get_model_entity_ids(model_ent_item)
      elif type_item.value(0) == 'enclosure-surface':
        surface_ids = CardFormat.get_model_entity_ids(model_ent_item)
        self.enclosure_surface_set_ids += surface_ids
        moving_item = att.itemAtPath('type/moving', '/')
        if moving_item and moving_item.isEnabled():
          self.moving_enclosure_surface_set_ids += surface_ids

    self.interface_set_ids.sort()
    self.enclosure_surface_set_ids.sort()
    self.moving_enclosure_surface_set_ids.sort()
    print 'interface_set_ids', self.interface_set_ids
    print 'enclosure_surface_set_ids', self.enclosure_surface_set_ids
    print 'moving_enclosure_surface_set_ids', self.moving_enclosure_surface_set_ids

    # Check for moving radiation
    att_list = self.sim_atts.findAttributes('enclosure-radiation')
    enclosure_att = att_list[0]
    enable_item = enclosure_att.find('enable')
    if enable_item.isEnabled():
      CardFormat.Conditions.add(ENCLOSURE_RADIATION)

      item = enclosure_att.itemAtPath('enable/enclosure/moving-radiation', '/')
      moving_item = smtk.attribute.to_concrete(item)
      #print 'moving_item value', moving_item.value(0)
      if moving_item.value(0) == 1:
        print 'Set condition', MOVING_RADIATION
        CardFormat.Conditions.add(MOVING_RADIATION)
