def test_validation_unknown_key(self): """Test error raised on unknown key.""" vol_elem = { 'grain_idx': np.random.randint(0, 9, (2, 2, 2)), 'bad_key': 1, } with self.assertRaises(ValueError): validate_volume_element(vol_elem)
def test_validation_grain_idx_value(self): """Test error raised on incorrect `grain_idx` dimension.""" vol_elem = {'grain_idx': np.random.randint(0, 9, (2, 2))} with self.assertRaises(ValueError): validate_volume_element(vol_elem)
def test_validation_missing_key(self): """Test error raised on missing mandatory key.""" vol_elem = {} with self.assertRaises(ValueError): validate_volume_element(vol_elem)
def write_geom(volume_element, geom_path): """Write the geometry file for a spectral DAMASK simulation. Parameters ---------- volume_element : dict Dict that represents the specification of a volume element, with keys: element_material_idx : ndarray of shape equal to `grid_size` of int, optional Determines the material to which each geometric model element belongs, where P is the number of elements. grid_size : ndarray of shape (3,) of int, optional Geometric model grid dimensions. size : list of length three, optional Volume element size. By default set to unit size: [1.0, 1.0, 1.0]. origin : list of length three, optional Volume element origin. By default: [0, 0, 0]. geom_path : str or Path The path to the file that will be generated. Returns ------- geom_path : Path The path to the generated file. Notes ----- The microstructure and texture parts are not included in the header of the generated file. """ volume_element = validate_volume_element(volume_element) element_material_idx = volume_element['element_material_idx'] grid_size = element_material_idx.shape ve_size = volume_element.get('size') or [1.0, 1.0, 1.0] ve_origin = volume_element.get('origin') or [0.0, 0.0, 0.0] num_micros = np.max( element_material_idx) + 1 # element_material_idx is zero-indexed header_lns = [ f'grid a {grid_size[0]} b {grid_size[1]} c {grid_size[2]}', f'size x {ve_size[0]} y {ve_size[1]} z {ve_size[2]}', f'origin x {ve_origin[0]} y {ve_origin[1]} z {ve_origin[2]}', f'microstructures {num_micros}', f'homogenization 1', ] num_header_lns = len(header_lns) header = f'{num_header_lns} header\n' + '\n'.join(header_lns) + '\n' elem_mat_idx_2D = np.concatenate(element_material_idx.swapaxes(0, 2)) elem_mat_idx_2D += 1 # one-indexed arr_str = '' for row in elem_mat_idx_2D: for col in row: arr_str += '{:<5d}'.format(col) arr_str += '\n' geom_path = Path(geom_path) with geom_path.open('w') as handle: handle.write(header + arr_str) return geom_path
def geom_to_volume_element(geom_path, phase_labels, homog_label, orientations=None): """Read a DAMASK geom file and parse to a volume element. Parameters ---------- geom_path : str or Path Path to the DAMASK geometry file. The geom file must include texture and microstructure parts in its header if `orientations` is None. phase_labels : list or ndarray of str, optional List of phase labels to associate with the constituents. The first list element is the phase label that will be associated with all of the geometrical elements for which an orientation is also specified. Additional list elements are phase labels for geometrical elements for which no orientations are specified. For instance, if the DAMASK command `geom_canvas` was used on a geom file to generate a new geom file that included additional material indices, the phases assigned to those additional material indices would be specified as additional list elements in `phase_labels`. homog_label : str, optional The homogenization scheme label to use for all materials in the volume element. orientations : dict, optional If specified, use these orientations instead of those that might be specified in the geometry file. Dict containing the following keys: type : str One of "euler", "quat". quaternions : (list or ndarray of shape (R, 4)) of float, optional Array of R row four-vectors of unit quaternions. Specify either `quaternions` or `euler_angles`. euler_angles : (list or ndarray of shape (R, 3)) of float, optional Array of R row three-vectors of Euler angles. Specify either `quaternions` or `euler_angles`. Specified as proper Euler angles in the Bunge convention (rotations are about Z, new-X, new-new-Z). euler_degrees : bool, optional If True, `euler_angles` are expected in degrees, rather than radians. unit_cell_alignment : dict Alignment of the unit cell. P : int, optional The "P" constant, either +1 or -1, as defined within [1]. Returns ------- volume_element : dict References ---------- [1] Rowenhorst, D, A D Rollett, G S Rohrer, M Groeber, M Jackson, P J Konijnenberg, and M De Graef. "Consistent Representations of and Conversions between 3D Rotations". Modelling and Simulation in Materials Science and Engineering 23, no. 8 (1 December 2015): 083501. https://doi.org/10.1088/0965-0393/23/8/083501. """ geom_dat = read_geom(geom_path) volume_element = { 'orientations': orientations or geom_dat['orientations'], 'element_material_idx': geom_dat['element_material_idx'], 'grid_size': geom_dat['grid_size'], 'size': geom_dat['size'], 'phase_labels': phase_labels, 'homog_label': homog_label, } volume_element = validate_volume_element(volume_element) return volume_element
def read_material(path): """Parse a DAMASK material.yaml input file. Parameters ---------- path : str or Path Path to the DAMASK material.yaml file. Returns ------- material_data : dict Parsed data from the DAMASK material.yaml file. Keys are: phases : dict The "phase" dict contained within the material file. homog_schemes : dict The "homogenization" dict contained within the material file. volume_element : dict Dict representing the volume element. The distribution of materials across the elements (i.e. keys `element_material_idx` and `grid_size`) are not included, since this information is not contained in the material file. With keys: constituent_material_idx : ndarray of shape (N,) of int Determines the material to which each constituent belongs, where N is the number of constituents. constituent_material_fraction: ndarray of shape (N,) of float The fraction that each constituent occupies within its respective material, where N is the number of constituents. constituent_phase_label : ndarray of shape (N,) of str Determines the phase label of each constituent, where N is the number of constituents. constituent_orientation_idx : ndarray of shape (N,) of int Determines the orientation (as an index into `orientations`) associated with each constituent, where N is the number of constituents. material_homog : ndarray of shape (M,) of str Determines the homogenization scheme (from a list of available homogenization schemes defined elsewhere) to which each material belongs, where M is the number of materials. orientations : dict Dict containing the following keys: type : str Value is "quat". quaternions : ndarray of shape (R, 4) of float Array of R row four-vectors of unit quaternions. """ yaml = YAML(typ='safe') material_dat = yaml.load(Path(path)) material_homog = [] const_material_idx = [] const_material_fraction = [] const_phase_label = [] const_orientation_idx = [] orientations = { 'type': 'quat', 'quaternions': [], } for mat_idx, material in enumerate(material_dat['microstructure']): material_homog.append(material['homogenization']) for const in material['constituents']: const_material_idx.append(mat_idx) const_material_fraction.append(const['fraction']) const_phase_label.append(const['phase']) orientations['quaternions'].append(const['orientation']) const_orientation_idx.append(len(const_orientation_idx)) vol_elem = { 'constituent_material_idx': const_material_idx, 'constituent_material_fraction': const_material_fraction, 'constituent_phase_label': const_phase_label, 'constituent_orientation_idx': const_orientation_idx, 'material_homog': material_homog, 'orientations': orientations, } material_data = { 'volume_element': vol_elem, 'phases': material_dat['phase'], 'homog_schemes': material_dat['homogenization'], } material_data['volume_element'] = validate_volume_element(**material_data) return material_data