import numpy as np
import pyvista as pv
from copy import deepcopy
from upxo._sup import dataTypeHandlers as dth
from upxo.pxtal.mcgs3_temporal_slice import mcgs3_grain_structure
from dataclasses import dataclass
[docs]
class OFHC_Cu_vox():
_slots = ("twin_setup", "twspec", "fdb")
__slots__ = _slots + mcgs3_grain_structure.__slots__
_propdtype_ = np.float32
_iddtype_ = np.int32
_common_mprops_ = True
[docs]
@dataclass
class TwinSetup:
nprops: int
mprops: list
twin_axis: tuple
[docs]
@dataclass
class TwinMorphSpec_v1:
n: list
tv: np.array
dlk: np.array
dnw: np.array
dno: np.array
tdis: str
dlk: dict
dlk: list
dlk: bool
[docs]
class FDB:
def __init__(self):
"""Initialise an empty feature data base container."""
pass
[docs]
@dataclass
class MPropSetup:
volnv: dict
rat_sanv_volnv: dict
def __init__(self, lgi=None, ea=None):
"""Initialise OFHC Cu voxel grain structure from a grain label image ``lgi`` and Euler angles ``ea``."""
super().__init__()
# -----------------------------------
_fid_ = np.unique(lgi)
self.fdb = {}
self.fdb = {'base': {'tess': lgi, 'ea': ea, 'fid': _fid_,
'n': _fid_.size,
'mprops': None,
'neigh_fid': None
}
}
del _fid_
# -----------------------------------
self.twin_setup = {'nprops': 2,
'mprops': {'volnv': {'use': True, 'reset': False,
'k': [.02, 1.0], 'min_vol': 4,},
'rat_sanv_volnv': {'use': True,
'reset': False,
'k': [0.0, .8],
'sanv_N': 26}, } }
# -----------------------------------
self.twspec = {'n': [5, 10, 3],
'tv': np.array([5, -3.5, 5]),
'dlk': np.array([1.0, -1.0, 1.0]),
'dnw': np.array([0.5, 0.5, 0.5]),
'dno': np.array([0.5, 0.5, 0.5]),
'tdis': 'normal',
'tpar': {'loc': 1.12, 'scale': 0.25, 'val': 1},
'vf': [0.05, 1.00],
'sep_bzcz': False}
# -----------------------------------
self.twgenspec = {'seedsel': 'random_gb', 'K': 10,
'bidir_tp': False, 'checks': [True, True],}
[docs]
def add_to_fdb(self, name, value):
"""
Add or update a named entry in the feature data base.
Examples
--------
.. code-block:: python
value = {'lgi': lgi,
'ea': ea,
'mprops': None,
'neigh_fid': None}
pxtal.add_to_fdb('base', value)
"""
if name == "base":
if "lgi" in value and value["lgi"]:
self.add_lgi(value['lgi'])
else:
KeyError("Value dictionary has no lgi key or no valid lgi key")
[docs]
def add_data_to_fdb(self, name, value, calc_flag):
"""Add or update a named field in the feature data base."""
if name == "lgi":
self.fdb['base']['lgi'] = value
elif name == "ea":
self.fdb['base']['lgi'] = value
elif name == "fid":
if self.fdb['base']['lgi']:
if calc_flag:
fid = np.unique(self.fdb['base']['lgi'])
self.fdb['base']['fid'] = fid
else:
pass
else:
raise ValueError("")
self.fdb['base']['lgi'] = value
elif name == "n":
self.fdb['base']['lgi'] = value
elif name == "mprops":
self.fdb['base']['lgi'] = value
elif name == "neigh_gid":
self.fdb['base']['lgi'] = value
[docs]
def setup_twins(self):
"""Alias for ``setup_for_twins``; defers to ``add_to_fdb``."""
add_to_fdb
[docs]
def setup_for_twins(self, nprops=2, mprops=None, instance_name='twin.0',
feature_name='annealing_twin', viz_grains=False,
opacity=1.0):
""" Carry out pre-requisite operations needed to establish twins """
print('Finding twin host grains.')
_gid_data_ = self.find_twin_hosts(nprops=nprops, mprops=mprops,
viz_grains=viz_grains,
opacity=opacity)
GIDS_masks_mprops, GIDS_mask, GIDS = _gid_data_
GIDS = GIDS
# -----------------------------------------------
print('\nSetting up the twin data structure')
self.setup_gid_twin(GIDS)
print(f'\nSetting Feature Data Base [---> {instance_name} <---]\n',
' for twinned grain structur4e instance.')
self.add_fdb(fname=instance_name,
dnames=('fid', 'feat_host_gids'),
datas=(deepcopy(self.lgi), GIDS),
info={'name': feature_name, 'mprops': mprops,
'vf_min': None, 'vf_max': None, 'vf_actual': None})
[docs]
def setup_gid_twin(self, GIDS):
"""Initialise ``self.gid_twin`` as a dict mapping each host grain id to ``None``."""
self.gid_twin = {gid: None for gid in GIDS}
[docs]
def add_fdb(self, *, fname, dnames, datas, info):
"""
Add feature data base entry.
Returns
-------
None
Examples
--------
.. code-block:: python
self.add_fdb(fname='twin_01',
dnames='fid',
datas=123,
info={'a': 1, 'b': 2})
Notes
-----
Intended for internal use.
"""
# initial Validations for fname
# -----------------------------------------
'''if fname in self.fdb.keys():
raise ValueError(f'[fname: {fname}] is an existing feature.',
'Use gstslice.reset_fdb(..) to reset.')'''
# -----------------------------------------
# Validations for infokeys
if not isinstance(info, dict):
raise ValueError('info must be a dictionary')
if not all([isinstance(info_, str) for info_ in info.keys()]):
raise ValueError('infokey_list are not all strings.')
# -----------------------------------------
if type(dnames) not in dth.dt.ITERABLES:
dnames = (dnames,)
if type(datas) not in dth.dt.ITERABLES:
datas = (datas,)
# -----------------------------------------
self.fdb[fname]['data'] = {}
for dname, data in zip(dnames, datas):
self.fdb[fname]['data'][dname] = data
self.fdb[fname]['info'] = info
[docs]
def find_twin_hosts(self, nprops=2,
mprops={'volnv': {'use': True, 'reset': False,
'k': [.1, .8], 'min_vol': 4,},
'rat_sanv_volnv': {'use': True, 'reset': False,
'k': [.1, .8], 'sanv_N': 26,}, },
viz_grains=False, opacity=1.0):
"""Identify grains eligible to host annealing twins based on morphological properties."""
if self._common_mprops_:
mprops = self.twin_setup['mprops']
# ---------------------------------------------------------------------
print(40*'-', '\nFinding grains which can host twins.\n')
# ---------------------------------------------------------------------
# Validate mprops
for mn in mprops.keys():
if mn not in ('volnv', 'rat_sanv_volnv', ):
print(' Invalid mprop names specified.')
print(' ONly volnv, rat_sanv_volnv allowed as of now.')
return None
# Validata mprop data existance
for mn in mprops.keys():
if mn == 'volnv' and mprops['volnv']['use']:
if mprops[mn]['reset'] or self.mprop[mn] is None:
print('VOLNV data being set or reset')
self.set_mprop_volnv()
if mn == 'rat_sanv_volnv' and mprops['rat_sanv_volnv']['use']:
N = mprops['rat_sanv_volnv']['sanv_N']
verbosity = self.n // 10
if self.mprop['sanv'] == None:
self.set_mprop_sanv(N=N, verbosity=verbosity)
if mprops[mn]['reset'] or self.mprop[mn] is None:
print('rat_sanv_volnv data being set or reset using N={N}')
self.set_mprop_rat_sanv_volnv(reset_volnv=False,
reset_sanv=False,
N=N, verbosity=verbosity)
print('\nmprops data validation pass.')
# ---------------------------------------------------------------------
# Find the actual number of properties to use based on user input
# mprop flag
nprops = np.sum([1 for mn in mprops.keys() if mprops[mn]['use']])
# ---------------------------------------------------------------------
GIDS_masks_mprops = np.full(nprops+1, None)
GIDS = {mn: None for mn in mprops.keys()}
# ---------------------------------------------------------------------
mprop_i = 0
for i, mn in enumerate(mprops.keys(), start=0):
if mn in ('volnv', 'rat_sanv_volnv') and mprops[mn]['use']:
print(f'Caclulaing gid_masks for mprop: {mn}.')
d = np.array(list(self.mprop[mn].values())) # Data
f = mprops[mn]['k'] # User defined factors
dmax = d.max() # Data maximum
dl = dmax*f[0] # Data low
dh = dmax*f[1] # Data high
GIDS_masks_mprops[i] = np.logical_and(d >= dl, d <= dh)
mprop_i += 1
# ---------------------------------------------------------------------
'''Identify multi-voxel grains'''
vol = np.array(list(self.mprop['volnv'].values()))
GIDS_masks_mprops[mprop_i] = vol >= mprops['volnv']['min_vol']
# ---------------------------------------------------------------------
GIDS_masks_mprops = np.stack(GIDS_masks_mprops, axis=1)
GIDS_mask = np.prod(GIDS_masks_mprops, axis=1).astype(bool)
GIDS = np.argwhere(GIDS_mask).T[0]+1
# ---------------------------------------------------------------------
if viz_grains:
self.make_pvgrid()
self.add_scalar_field_to_pvgrid(sf_name="lgi", sf_value=None)
self.plot_grains(GIDS+1, opacity=opacity, show_edges=False)
# ---------------------------------------------------------------------
return GIDS_masks_mprops, GIDS_mask, GIDS
[docs]
def set_mprop_volnv(self):
"""Calculate the volume by number of voxels."""
print(40*"-", "\nSetting grain volumes (metric: 'volnv') -> ")
unique_counts = np.bincount(self.lgi.ravel(), minlength=self.n+1)
self.mprop['volnv'] = {gid + 1: unique_counts[gid + 1] for gid in range(self.n)}
print("Grain volumes (metric: 'volnv') -> have been set.\n", 40*"-")
[docs]
def set_mprop_sanv(self, N=26, verbosity=100):
"""Calculate the total surface area by number of voxels."""
print("\nCalculating feature surface areas (metric: 'sanv').")
sanv = [None for gid in self.gid]
_r = np.sqrt(3)*1.00001
for gid in self.gid:
if gid % verbosity == 0:
print(f"Set gstslice[{self.m}].mprop['sanv'] for gid:{gid}/{self.gid[-1]}")
'''Get the bounding box lgi of this grain'''
BBLGI = self.find_bounding_cube_gid(gid)
'''Find the locations of grain voxels in the bounding box'''
BBLGI_locs = np.argwhere(BBLGI == gid)
'''Construct tree of the grain voxel locations'''
BBLGI_locstree = self._ckdtree_(BBLGI_locs)
'''Find the number of nearest neighbours of every voxel in the grain'''
neighbor_counts = BBLGI_locstree.query_ball_point(BBLGI_locs,
r=_r,
return_length=True)
'''Boundary coordinates are those which have less than 26 neighbours'''
boundary_coords = BBLGI_locs[neighbor_counts < N]
sanv[gid-1] = boundary_coords.shape[0]
self.mprop['sanv'] = {gid: sanv for gid, sanv in zip(self.gid, sanv)}
print("Finished setting grain surface areas (metric: 'sanv').")
[docs]
def set_mprop_rat_sanv_volnv(self,
reset_volnv=False,
reset_sanv=False,
N=26,
verbosity=100):
"""Compute surface-area-to-volume ratio metric ``rat_sanv_volnv`` for each grain."""
# --------------------------------
print('\nCalculating mprop metric: rat_sanv_volnv')
if reset_volnv or self.mprop['volnv'] == None:
print("self.mprop['volnv'] data being set or reset")
self.set_mprop_volnv()
# --------------------------------
if reset_sanv or self.mprop['sanv'] == None:
print("self.mprop['sanv'] data being set or reset using N={N}")
self.set_mprop_sanv(N=N, verbosity=verbosity)
# --------------------------------
self.mprop['rat_sanv_volnv'] = {gid: s/v
for gid, s, v in zip(self.gid,
self.mprop['volnv'].values(),
self.mprop['sanv'].values())}
[docs]
def instantiate_twins(self,
ninstances=2,
base_gs_name_prefix='twin.',
reset_fdb=True,
reset_keystring='twin.',
make_sep_pvgrds=False,
save_twin_coords=False,
clean_verbosity_interval_1=250
):
"""
# -----> FEATURE IDS
gstslice.fdb["twin.0"]["data"]["feat_host_gids"] This has all gids
initialliy selected for introducing twins. For grain ids which actually
host twins, refer gstslice.fdb["twin.0"]["data"]["feat_host_ids"]
instead.
gstslice.fdb["twin.0"]["data"]["feat_host_ids"] Feature (i.e. grain)
ids which actually host twins.
gstslice.fdb["twin.0"]["data"]["twin_map_g_t_missed"] Numpy array
containing the list of parent feature IDs which were misses duringh
twin instantiation. These are featre IDs initially selected for twin
generation, but later rejected as twins could not be produced with the
user specified algorithm control parameter values.
gstslice.fdb["twin.0"]["data"]["notwin_gids"] Parent features i.e.
grains, which were not selected to parent any children i.e. twins.
gstslice.fdb["twin.0"]["data"]["twin_id"] Twin ID numbers
gstslice.fdb["twin.0"]["data"]["twin_map_g_t"] Contains parent to
children map. As a dictionary, keys are parent feature IDs. A value is
a list of children feature IDs, which in this case are twin IDs.
gstslice.fdb["twin.0"]["data"]["map_cp"] Dictiobnary having keys as
twin IDs and values as parent IDs. This is a reverse map of twin ID
to parent ID. len(gstslice.fdb["twin.0"]["data"]["map_cp"].keys())
gives the total number of children, which can also abe obtained as
sum(gstslice.fdb["twin.0"]["data"]["twin_map_g_nt"].values()). In fact,
in some UPXO twined grain structure,
len(gstslice.fdb["twin.0"]["data"]["map_cp"].keys()) was 383,
sum(gstslice.fdb["twin.0"]["data"]["twin_map_g_nt"].values()) was also
383 and len(gstslice.fdb["twin.0"]["data"]["twin_id"]) was also 383.
# -----------------------------------------
# -----> DEPRECATED
gstslice.fdb["twin.0"]["data"]["twin_i"] DEPRECATED. Commented in code.
gstslice.fdb["twin.0"]["data"]["parent_id"] numpy array of parent IDs
which actually host children features. DEPRECTAED. This is the same as
gstslice.fdb["twin.0"]["data"]["feat_host_ids"]. Commented out in code.
gstslice.fdb["twin.0"]["data"]["twin_zero_voxels"] List of twin IDs
with zero voxels. Should be empty. To be deprecated.
# -----------------------------------------
# -----> COORDINATES
gstslice.fdb["twin.0"]["data"]["twin_coords"] Coordinates of twins.
len(gstslice.fdb["twin.0"]["data"]["twin_coords"]) is same as
len(gstslice.fdb["twin.0"]["data"]["parent_id"]).
gstslice.fdb["twin.0"]["data"]["twin_map_g_t_coords"] Contains voxel
coordinates of the twinned regions. Keys are parent feature ID. Value
is dict with keys as children feature IDs and values being
corresponding numpy cooridnate arrays.
# -----------------------------------------
# -----> SIZE AND CONTENT PROPERTIES
gstslice.fdb["twin.0"]["data"]["twin_vol"] Twin volumes
gstslice.fdb["twin.0"]["data"]["twin_vf"] Twin volume fractions
gstslice.fdb["twin.0"]["data"]["twin_map_g_nt"] Contains parent to
children count map. As a dictionary, keys are parent feature IDs. A
value is the length of the list of children feature IDs.
gstslice.fdb["twin.0"]["data"]["twin_vol_total"] This is total volume
of twins across the entire domain. It is numpy.int32 type.
gstslice.fdb["twin.0"]["data"]["twin_vf_total"] This is the overall
volume fraction of twins.
gstslice.fdb["twin.0"]["data"]["twin_map_g_t_nvox"] Contains parent to
children voxel count map. As a dictionary, keys are parent feature IDs.
A value is the list of total number of voxels in each child feature.
List size will be the total child feature count for the current parent
feature ID.
gstslice.fdb["twin.0"]["data"]["twin_nvox"] Total number of voxels
across all children features (dict value) for a given parent feature
ID (dict key).
# -----------------------------------------
gstslice.fdb["twin.0"]["data"]["pvgrid"] This is the PyVista grid
having gstslice.fdb["twin.0"]["data"]["fid"] as scalar field.
gstslice.fdb["twin.0"]["data"]["parent_feature"] String value
containing, the name of the parent feature, which in this case is
'grain'.
"""
if not isinstance(ninstances, int):
raise ValueError('Invalid ninstances input.')
if type(base_gs_name_prefix) in dth.dt.NUMBERS:
base_gs_name_prefix = str(base_gs_name_prefix) + '.'
if not isinstance(base_gs_name_prefix, str):
raise ValueError('Invalid base_gs_name_prefix input.')
instance_names = []
# -----------------------------------------------------------
'''Wipe the slate clean.'''
if reset_fdb:
for key in list(self.fdb.keys()):
if key.startswith(reset_keystring):
del self.fdb[key]
# -----------------------------------------------------------
for inst in range(ninstances):
print(50*'#', 5*'\n',
f'Creating instance: {inst+1} of {ninstances}',
5*'\n', 50*'#')
instance_name = base_gs_name_prefix+str(inst)
instance_names.append(instance_name)
self.setup_for_twins(nprops=self.twin_setup['nprops'],
mprops=self.twin_setup['mprops'],
instance_name=instance_name,
viz_grains=False)
self.identify_twins(base_gs_name=instance_name,
twspec=self.twspec,
twgenspec=self.twgenspec,
viz=False)
if self.pvgrid is not None:
self.add_scalar_field_to_pvgrid(sf_name=instance_name,
sf_value=self.fdb[instance_name]['data']['fid'])
# -----------------------------------------------------------
self.fdb[instance_name]['info']['pvgrid'] = False
if make_sep_pvgrds:
pvgrid = pv.ImageData()
pvgrid.dimensions = np.array(self.lgi.shape) + 1
pvgrid.origin = (0, 0, 0)
pvgrid.spacing = (1, 1, 1)
pvgrid.cell_data['fid'] = self.fdb[instance_name]['data']['fid'].flatten(order="F")
self.fdb[instance_name]['data']['pvgrid'] = pvgrid
self.fdb[instance_name]['info']['pvgrid'] = True
# -----------------------------------------------------------
'''
1. Clean up the self.fdb[instance_name]['data']['twin_map_g_t'] data.
It contains lots of keys witgh [] values. Keys reprezsent parent grain
IDs and value represents list of twin IDs. The empty bvalues means twin
creation proicess was unseccessful for the given gid and therefore a
missed attempt. No need to record the missed parent grain in this data.
2. Build data for the number of twins contained by a parent grain.
3. Calcuklate number of voxels of each non zero voxel twin.
4. Extract twin IDs having zero voxels. I think I saw a unique case
where one of the twin had zero voxel locations. Never happened again so
far. To be on the safewr side, I have introduced this to record any
such instance. May be not needed. For later deascrewtion. Will move on
taking this along fornow. VARIABLE TO BE TRUNCATED ?
'''
for instance_name in instance_names:
print(40*'#')
print(f"Data cleaning underway for twinning instance {instance_name}.")
'''Parent grain to twin ID map'''
twin_map_g_t = {}
'''Number of twins.'''
twin_map_g_nt = {}
'''Voxel coordinates of all twins'''
twin_map_g_t_coords = {}
'''Number of voxels of each twin'''
twin_map_g_t_nvox = {}
'''Parent gids with missed twin generation attempts.'''
twin_map_g_t_missed = []
'''Twin IDs with zero voxels. VARIABLE TO BE TRUNCATED ?'''
twin_zero_voxels = []
# Number of pids: npids
# pid_count: iteration variable, increments ny a unit every iteration.
npids, pid_count = self.fdb[instance_name]['data']['feat_host_gids'].size, 1
for pid, twin_ids in self.fdb[instance_name]['data']['twin_map_g_t'].items():
if pid_count % clean_verbosity_interval_1 == 0:
print(f".... parent grain number {pid_count} of {npids}")
if len(twin_ids) > 0:
twcoords = [self.find_feature_voxel_locs(fname=instance_name,
fids=[twid],
verbosity=10,
printmsg=False,
saa=False,
throw=True)
for twid in twin_ids]
twin_map_g_t__pid = []
twin_map_g_t_coords__pid = {}
nvox = []
for twid, twc in zip(twin_ids, twcoords):
if twc[twid].shape[0] > 0:
twin_map_g_t__pid.append(twid)
twin_map_g_t_coords__pid[twid] = twc[twid]
nvox.append(twc[twid].shape[0])
if sum(twin_map_g_t__pid) > 0:
twin_map_g_t[pid] = twin_map_g_t__pid
twin_map_g_nt[pid] = len(twin_map_g_t__pid)
twin_map_g_t_coords[pid] = twin_map_g_t_coords__pid
twin_map_g_t_nvox[pid] = nvox
else:
twin_zero_voxels.append(twid)
else:
twin_map_g_t_missed.append(pid)
pid_count += 1
twin_map_g_t_missed = np.array(twin_map_g_t_missed)
# The parent grains which actually host twins are:
hosts = np.array(list(twin_map_g_t.keys()))
# Clean the twin _coordinates data base as it contains parent IDs
# which were actually missed during the twin inclusion process.
self.fdb[instance_name]["data"]["twin_coords"] = {k: v for k, v in self.fdb["twin.0"]["data"]["twin_coords"].items() if v != None}
# Correct twin volume data. Just deprecating it through deletion,
# I dont want to delete at source as of now. May be later. Volumes
# already present in twin_nvox
del self.fdb[instance_name]["data"]["twin_vol"]
self.fdb[instance_name]['data']['feat_host_ids'] = hosts
self.fdb[instance_name]['data']['twin_map_g_t'] = twin_map_g_t
self.fdb[instance_name]['data']['twin_map_g_nt'] = twin_map_g_nt
self.fdb[instance_name]['data']['twin_map_g_t_nvox'] = twin_map_g_t_nvox
self.fdb[instance_name]['data']['twin_nvox_sum'] = np.array([sum(nv) for nv in twin_map_g_t_nvox.values()])
D = {}
for key, val in self.fdb[instance_name]['data']['twin_map_g_t'].items():
for fid_count, fid_ in enumerate(val):
D[fid_] = self.fdb[instance_name]['data']['twin_map_g_t_nvox'][key][fid_count]
self.fdb[instance_name]['data']['twin_nvox'] = D
if save_twin_coords:
self.fdb[instance_name]['data']['twin_map_g_t_coords'] = twin_map_g_t_coords
self.fdb[instance_name]['data']['twin_map_g_t_missed'] = twin_map_g_t_missed
self.fdb[instance_name]['data']['twin_zero_voxels'] = twin_zero_voxels
self.fdb[instance_name]['data']['twin_vol_total'] = sum(self.fdb[instance_name]['data']['twin_nvox'])
parent_ids = self.fdb[instance_name]['data']['twin_map_g_t_nvox'].keys()
# Correct twin volume fraction data and just re-writing it!!
parent_vols = np.array([self.grain_locs[pid].shape[0] for pid in parent_ids])
tw_vfs = np.array([twvol/pgrainvol for twvol, pgrainvol in zip(self.fdb[instance_name]['data']['twin_nvox'], parent_vols)])
self.fdb[instance_name]['data']['twin_vf'] = tw_vfs
self.fdb[instance_name]['data']['twin_vf_total'] = self.fdb[instance_name]['data']['twin_vol_total'] / self.domvol
print('\n', f"\nTwin volume fraction: {self.fdb[instance_name]['data']['twin_vf_total']}")
# -------------------------------------------------
"""
NOTE: THE FOLLOWING TWO MUST BE UPDATED. FOR LATER WORK. I will set
these to None for now.
"""
self.fdb[instance_name]['data']['twin_id'] = np.hstack([twids for twids in self.fdb[instance_name]['data']['twin_map_g_t'].values()])
# self.fdb[instance_name]['data']['twin_i'] = list(range(self.fdb[instance_name]['data']['twin_id'].size))
# self.fdb[instance_name]['data']['parent_id'] = np.array(list(self.fdb[instance_name]['data']['twin_map_g_t'].keys()))
self.fdb[instance_name]['data']['parent_feature'] = 'grain'
# Set ip Child to parnet mapping
self.fdb[instance_name]['data']['map_cp'] = {cid: None for cid in self.fdb[instance_name]['data']['twin_id']}
for pid, cids in self.fdb[instance_name]['data']['twin_map_g_t'].items():
for cid in cids:
self.fdb[instance_name]['data']['map_cp'][cid] = pid
'''
Start the sanity checks.
'''
print("\n\nPerforming sanity checks")
fails = 0
for i, host in enumerate(self.fdb[instance_name]['data']['feat_host_ids']):
if i % 500 == 0:
print(f"host {i} of {len(self.fdb[instance_name]['data']['feat_host_ids'])}")
twin_ids = twin_map_g_t[host]
# Number of voxels in the parent grain id before twin introduction
a = np.where(self.lgi==host)[0].size
# Number of voxels in the parent grain id after twin introduction
b = np.where(self.fdb[instance_name]['data']['fid']==host)[0].size
# Number of voxels in individual twins
c = [np.where(self.fdb[instance_name]['data']['fid']==twid)[0].size
for twid in twin_ids]
# Residual number of voxels, MUST be zero.
if a-(b+sum(c)) != 0:
# <--- This completes sanity check number 1
fails += 1
# Number of parent voxels
npvox = self.grain_locs[host].shape[0]
# Number of twin voxels
twsvox = twin_map_g_t_nvox[host]
# Number of voxels in the parent grain id after twin introduction
# thatis, the residual number of voxels
d = npvox-sum(twsvox)
# This must be the same as b
if d != b:
fails += 1
# <--- This completes the sanity check number 2
print(f"........ {2*(len(hosts)-fails)} of {2*len(hosts)} sanity checks passed.")
print(f"Performing additional checks.")
a = len(self.fdb[instance_name]["data"]["twin_vf"])
b = len(self.fdb[instance_name]["data"]["twin_map_g_t"])
c = len(self.fdb[instance_name]["data"]["twin_map_g_nt"])
d = len(self.fdb[instance_name]["data"]["twin_map_g_t_nvox"])
e = len(self.fdb[instance_name]["data"]["twin_map_g_t_coords"])
f = len(self.fdb[instance_name]["data"]["twin_nvox"])
if a == b == c == d == e == f == 1:
print('Array length equality check 1 passed.')
else:
print('Array length equality check 1 failed.')
a = len(self.fdb[instance_name]["data"]['feat_host_gids'])
b = len(self.fdb[instance_name]["data"]["twin_map_g_t_missed"])
c = len(self.fdb[instance_name]["data"]["feat_host_ids"])
if a == b+c:
print('Array length equality check 2 passed.')
else:
print('Array length equality check 2 failed.')
print(40*'#')