def _calc_pop_fracs(n_dim, phases, sample_rng_seeds, max_int): # compute volume of each phase vol_rng = sample_rng_seeds['fraction'] n_phases = len(phases) rel_vols = np.ones(n_phases) for i, phase in enumerate(phases): vol = phase.get('fraction', 1) try: v_sample = -1 while v_sample < 0: v_sample = vol.rvs(random_state=vol_rng) vol_rng = (vol_rng + 1) % max_int rel_vols[i] = v_sample except AttributeError: rel_vols[i] = vol vol_fracs = rel_vols / sum(rel_vols) # Compute the average grain volume of each phase if n_dim == 2: avg_vols = [ geometry.factory(p['shape']).area_expectation(**p) for p in phases ] else: avg_vols = [ geometry.factory(p['shape']).volume_expectation(**p) for p in phases ] weights = vol_fracs / np.array(avg_vols) pop_fracs = weights / sum(weights) return pop_fracs
def _get_n_dim(phases): n_dim = None for phase in phases: if 'shape' in phase: n_dim = geometry.factory(phase['shape']).n_dim if n_dim is None: e_str = 'Number of dimensions could not be determined from phase ' e_str += 'shapes. Consider setting the shape of a phase, or' e_str += ' specifying the number of dimensions.' raise ValueError(e_str) return n_dim
def read_input(filename): """Convert input file to dictionary This function reads an input file and parses it into a dictionary. Args: filename (str): The name of an XML input file. Returns: collections.OrderedDict: Dictionary of run inputs. """ # Read in the file file_path = os.path.dirname(filename) with open(filename, 'r') as file: file_dict = xmltodict.parse(file.read()) assert 'input' in file_dict, 'Root <input> not found in input file.' in_data = dict_convert(file_dict['input'], file_path) k_args = ('material', 'domain') for arg in k_args: assert arg in in_data # Get domain domain_data = in_data['domain'] domain_shape = domain_data['shape'] domain_kwargs = {k: v for k, v in domain_data.items() if k != 'shape'} domain = geometry.factory(domain_shape, **domain_kwargs) in_data['domain'] = domain # Default settings kwargs = in_data.get('settings', {}) run_dir = kwargs.get('directory', '.') if not os.path.isabs(run_dir): rel_path = os.path.join(file_path, run_dir) kwargs['directory'] = os.path.expanduser(os.path.normpath(rel_path)) in_data['settings'] = kwargs return in_data
def factory(cls, seed_type, phase=0, breakdown=None, position=None, **kwargs): """Factory method for seeds This function returns a seed based on the seed type and keyword arguments associated with that type. The currently supported types are: * circle * ellipse * ellipsoid * rectangle * sphere * square If the seed_type is not on this list, an error is thrown. Args: seed_type (str): type of seed, from list above. phase (int): *(optional)* Material phase number of seed. Defaults to 0. breakdown (list or numpy.ndarray): *(optional)* List of circles or spheres that approximate the geometry. The list should be formatted as follows:: breakdown = [(x1, y1, z1, r1), (x2, y2, z2, r2), ...] The breakdown will be automatically generated if not provided. position (list or numpy.ndarray): *(optional)* The coordinates of the seed. Default is the origin. **kwargs: Keyword arguments that define the size, shape, etc of the seed geometry. Returns: Seed: An instance of the class. """ assert type(seed_type) is str seed_type = seed_type.strip().lower() if seed_type == 'nonetype': n_dim = 0 else: n_dim = geometry.factory(seed_type).n_dim if 'volume' in kwargs: if n_dim == 2: size = 2 * np.sqrt(kwargs['volume'] / np.pi) else: size = 2 * np.cbrt(3 * kwargs['volume'] / (4 * np.pi)) kwargs['size'] = size # Catch NoneType geometries if seed_type == 'nonetype': geom = None else: geom = geometry.factory(seed_type, **kwargs) if breakdown is None: if seed_type in ('circle', 'sphere'): breakdown = np.append(geom.center, geom.r).reshape(1, -1) else: breakdown = geom.approximate() if position is None: position = [0 for _ in range(geom.n_dim)] return cls(geom, phase, breakdown, position)
def from_info(cls, phases, volume, rng_seeds={}): """Create seed list from microstructure information This function creates a seed list from information about the microstruture. The "phases" input should be a list of material phase dictionaries, formatted according to the :ref:`phase_dict_guide` guide. The "volume" input is the minimum volume of the list of seeds. Seeds will be added to the list until this volume threshold is crossed. Finally, the "rng_seeds" input is a dictionary of random number generator (RNG) seeds for each parameter of the seed geometries. For example, if one of the phases uses "size" to define the seeds, then "size" could be a keyword of the "rng_seeds" input. The value should be a non-negative integer, to seed the RNG for size. The default RNG seed is 0. Note: If two or more parameters have the same RNG seed and the same kernel of the distribution, those parameters will **not** be correlated. This method updates RNG seeds based on the order that distributions are sampled to avoid correlation between independent random variables. Args: phases (dict): Dictionary of phase information, see :ref:`phase_dict_guide` for a guide. volume (float): The total area/volume of the seeds in the list. rng_seeds (dict): *(optional)* Dictionary of RNG seeds for each step in the seeding process. The dictionary keys should match shape parameters in ``phases``. For example:: rng_seeds = { 'size': 0, 'angle': 3, } Returns: SeedList: An instance of the class containing seeds prescribed by the phase information and filling the given volume. """ # determine dimensionality, set default shape default_shapes = {2: 'circle', 3: 'sphere'} n_dim = None if isinstance(phases, dict): phases = [phases] for phase in phases: if 'shape' in phase: n_dim = geometry.factory(phase['shape']).n_dim if n_dim is None: e_str = 'Number of dimensions could not be determined from phase ' e_str += 'shapes. Consider setting the shape of a phase, or' e_str += ' specifying the number of dimensions.' raise ValueError(e_str) for phase in phases: if 'shape' in phase: assert geometry.factory(phase['shape']).n_dim == n_dim else: phase['shape'] = default_shapes[n_dim] # compute volume of each phase vol_rng = rng_seeds.get('fraction', 0) np.random.seed(vol_rng) n_phases = len(phases) rel_vols = np.ones(n_phases) for i, phase in enumerate(phases): vol = phase.get('fraction', 1) try: v_sample = -1 while v_sample < 0: v_sample = vol.rvs() rel_vols[i] = v_sample except AttributeError: rel_vols[i] = vol vol_fracs = rel_vols / sum(rel_vols) phase_vols = volume * vol_fracs # compute number of seeds for each phase if n_dim == 2: avg_vols = [ geometry.factory(p['shape']).area_expectation(**p) for p in phases ] else: avg_vols = [ geometry.factory(p['shape']).volume_expectation(**p) for p in phases ] weights = phase_vols / np.array(avg_vols) pop_fracs = weights / sum(weights) seed_vol = 0 seeds = [] max_int = np.iinfo(np.int32).max while seed_vol < volume: # Pick the phase rng_seed = rng_seeds.get('phase', 0) np.random.seed(rng_seed) phase_num = np.random.choice(n_phases, p=pop_fracs) phase = phases[phase_num] rng_seeds['phase'] = np.random.randint(max_int) # Create the seed seed_shape = phase['shape'] seed_args = {'phase': phase_num} kw_n = 0 for kw in set(phase) - set(_misc.gen_kws): # set the RNG seed rng_seed = rng_seeds.get(kw, 0) np.random.seed(rng_seed) # Sample, with special cases for orientation if kw not in _misc.ori_kws: try: val = phase[kw].rvs(random_state=rng_seed) except AttributeError: val = phase[kw] seed_args[kw] = val elif (phase[kw] == 'random') and (n_dim == 2): np.random.seed(rng_seed) seed_args['angle_deg'] = 360 * np.random.rand() elif phase[kw] == 'random': np.random.seed(rng_seed) elems = np.random.normal(size=4) mag = np.linalg.norm(elems) elems /= mag val = Quaternion(elems).rotation_matrix seed_args[kw] = val elif kw in ['rot_seq', 'rot_seq_deg', 'rot_seq_rad']: seq = [] val = phase[kw] if not isinstance(val, list): val = [val] for rotation in val: rot_dict = {str(kw): rotation[kw] for kw in rotation} ax = rot_dict.get('axis', 'x') ang_dist = rot_dict.get('angle', 0) try: ang = ang_dist.rvs(random_state=rng_seed) except AttributeError: ang = ang_dist seq.append((ax, ang)) seed_args[kw] = seq else: try: val = phase[kw].rvs(random_state=rng_seed) except AttributeError: val = phase[kw] seed_args[kw] = val # Update the RNG seed np.random.seed(rng_seed + kw_n) rng_seeds[kw] = np.random.randint(max_int - kw_n) kw_n += 1 # Add seed to list seed = _seed.Seed.factory(seed_shape, **seed_args) seeds.append(seed) seed_vol += seed.volume return cls(seeds)