Exemple #1
0
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
Exemple #2
0
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
Exemple #3
0
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)
Exemple #5
0
    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)