def test_grid_instance_is_initialised( self, mock_Grid_Point, mock_index ): #volumes = [ 0.025, 0.025, 0.025, 0.025, 0.025 ] #mock_volumes.return_value = volumes mock_index.side_effect = [ 1, 3 ] mock_grid_points = [ Mock( spec=Grid_Point ), Mock( spec=Grid_Point ), Mock( spec=Grid_Point ), Mock( spec=Grid_Point ), Mock( spec=Grid_Point ) ] for g in mock_grid_points: g.sites = [] mock_Grid_Point.side_effect = mock_grid_points x_coordinates = np.array( [ 0.0, 1.0, 2.0, 3.0, 4.0 ] ) b = 0.25 c = 0.1 limits = [ 1.0, 1.0 ] limits_for_laplacian = [1.0, 1.0] set_of_sites = MagicMock( spec=Set_of_Sites ) sites = [ Mock( spec=Site ), Mock( spec=Site ) ] sites[0].x = 1.0 sites[1].x = 3.0 sites[0].defect_species = [ Mock( spec=DefectSpecies ) ] sites[1].defect_species = [ Mock( spec=DefectSpecies ) ] set_of_sites.__iter__.return_value = iter( sites ) grid = Grid( x_coordinates=x_coordinates, b=b, c=c, limits=limits, limits_for_laplacian=limits_for_laplacian, set_of_sites=set_of_sites ) self.assertEqual( grid.points, mock_grid_points ) self.x = x_coordinates self.limits = limits self.limits_for_laplacian = limits_for_laplacian expected_sites_at_grid_points = [ [], [ sites[0] ], [], [ sites[1] ], [] ] for p, e in zip( grid.points, expected_sites_at_grid_points ): self.assertEqual( p.sites, e ) for defect_species_list in [ sites[0].defect_species, sites[1].defect_species ]: for defect_species in defect_species_list: self.assertEqual( defect_species in grid.defect_species, True )
def form_continuum_sites( all_sites, x_min, x_max, n_points, b, c, defect_species, limits_for_laplacian, site_labels, defect_labels ): """ Creates a Set_of_Sites object for sites interpolated onto a regular grid, this is equivalent to assuming a continuum approximation. Args: all_sites (object): Orginal Set_of_Sites object from full data. x_min (float): Minimum x coordinate value defining the calculation region. x_max (float): Maximum x coordinate value defining the calculation region. n_points (int): Number of points that the data should be interpolated on to. b (float): b dimension for every grid point. c (float): c dimension for every grid point. defect_species (object): Class object containing information about the defect species present in the system. limits for laplacian (list): distance between the endmost sites and the midpoint of the next site outside of the calculation region for the first and last sites respectively. site_labels( list ): List of strings for the different site species. defect_labels (list): List of strings for the different defect species. Returns: :obj:`Set_of_Sites`: Sites interpolated onto a regular grid. """ grid = np.linspace( x_min, x_max, n_points ) limits = [grid[1] - grid[0], grid[1] - grid[0]] sites = [] for label, d_label in zip(site_labels, defect_labels): scaling = len( all_sites.subset( label ) ) / len( grid ) continuum_grid = Grid( grid, b, c, limits, limits_for_laplacian, all_sites.subset( label ) ) average_energies = np.array( [ site.average_local_energy( method = 'mean' )[0] for site in all_sites.subset( label ) ] ) new_energies = griddata( ( [ site.x for site in all_sites.subset( label ) ] ), average_energies, grid, method = 'nearest' ) for x, e in zip( grid, new_energies): sites.append( Site( label, x, [ defect_species[ d_label ] ], [e], scaling = np.array( scaling ) ) ) return Set_of_Sites( sites ), limits
def form_continuum_sites_new(all_sites, x_min, x_max, n_points, b, c, defect_species, limits_for_laplacian, site_labels, defect_labels): limits = [x_min, x_max] grid = np.linspace(x_min, x_max, n_points) sites = [] for label, d_label in zip(site_labels, defect_labels): scaling = len(all_sites.subset(label)) / len(grid) continuum_grid = Grid(grid, b, c, limits, limits_for_laplacian, all_sites.subset(label)) average_energies = np.array([ site.average_local_energy(method='mean')[0] for site in all_sites.subset(label) ]) new_energies = griddata( ([site.x for site in all_sites.subset(label)]), Vo.average_energies, grid, method='nearest') for x, e in zip(grid, new_energies): sites.append( Site(label, x, [defect_species[d_label]], [e], scaling=np.array(scaling))) return Set_of_Sites(sites)
def calculate_mobile_defect_conductivities(self, pos_or_neg_scr, scr_limit, species, mobility_scaling=False): """Calculate the conductivity ratio between the space charge region and the bulk both perpendicular and parallel to the grain boundary. A `Set_of_Sites` object is created for the sites in the space charge region, and the defect distributions calculated. The width of the space charge region is calculated and a bulk region of the same width is defined. A Set_of_Sites object for the bulk region is created and the defect distributions calculated. Taking each site as a resistor in series or parallel respectively, the conductivity is calculated and the ratio between the space charge region and the bulk is taken. Args: pos_or_neg_scr (str): 'positive' - for a positive space charge potential. 'negative' - for a negative space charge potential. scr_limit (float): The minimum electrostatic potential that the electrostatic potential must exceed to be included in the space charge region. species (str): The species for which the conductivity is being calculated. mobility_scaling (bool): For particles on a lattice which only interact through volume exclusion, the mobility exhibits a blocking term. True if the blocking term is to be included, False if the blocking term is not to be included. Default = False. Returns: float: The perpendicular conductivity ratio. The conductivity ratio between the bulk and the space charge region perpendicular to the grain boundary. float: The parallel conductivity ratio. The conductivity ratio between the bulk and the space charge region parallel to the grain boundary. """ space_charge_region = self.create_space_charge_region( self.subgrids[species], pos_or_neg_scr, scr_limit) space_charge_region_limits = self.calculate_offset( self.subgrids[species], np.min(space_charge_region), np.max(space_charge_region)) space_charge_region_sites = self.create_subregion_sites( self.subgrids[species], np.min(space_charge_region), np.max(space_charge_region)) for site in space_charge_region_sites: charge = site.defects[0].valence mobilities = site.defects[0].mobility space_charge_region_grid = Grid.grid_from_set_of_sites( space_charge_region_sites, space_charge_region_limits, space_charge_region_limits, self.grid.b, self.grid.c) space_charge_region_width = space_charge_region_grid.x[ -1] - space_charge_region_grid.x[0] mobile_defect_density = Set_of_Sites( self.subgrids[species].set_of_sites ).subgrid_calculate_defect_density(self.subgrids[species], self.grid, self.phi, self.temp) space_charge_region_mobile_defect_mf = space_charge_region_sites.calculate_probabilities( space_charge_region_grid, self.phi, self.temp) space_charge_region_mobile_defect_density = space_charge_region_sites.subgrid_calculate_defect_density( space_charge_region_grid, self.grid, self.phi, self.temp) if mobility_scaling: mobile_defect_conductivity = space_charge_region_mobile_defect_density * ( 1 - space_charge_region_mobile_defect_mf) * charge * mobilities else: mobile_defect_conductivity = space_charge_region_mobile_defect_density * charge * mobilities bulk_x_max = self.bulk_x_min + space_charge_region_width min_bulk_index, max_bulk_index = self.find_index( self.subgrids[species], self.bulk_x_min, bulk_x_max) self.bulk_limits = self.calculate_offset(self.subgrids[species], self.bulk_x_min, bulk_x_max) bulk_mobile_defect_sites = self.create_subregion_sites( self.subgrids[species], self.bulk_x_min, bulk_x_max) bulk_mobile_defect_grid = Grid.grid_from_set_of_sites( bulk_mobile_defect_sites, self.bulk_limits, self.bulk_limits, self.grid.b, self.grid.c) bulk_mobile_defect_density = Set_of_Sites( bulk_mobile_defect_grid.set_of_sites ).subgrid_calculate_defect_density(bulk_mobile_defect_grid, self.grid, self.phi, self.temp) bulk_region_mobile_defect_mf = bulk_mobile_defect_sites.calculate_probabilities( bulk_mobile_defect_grid, self.phi, self.temp) if mobility_scaling: bulk_mobile_defect_conductivity = bulk_mobile_defect_density * charge * mobilities else: bulk_mobile_defect_conductivity = bulk_mobile_defect_density * charge * mobilities * ( 1 - bulk_region_mobile_defect_mf) space_charge_array = np.column_stack( (mobile_defect_conductivity, space_charge_region_grid.x)) bulk_array = np.column_stack( (bulk_mobile_defect_conductivity, bulk_mobile_defect_grid.x)) if mobilities != 0.0: space_charge_perpendicular = sum(space_charge_region_grid.delta_x / mobile_defect_conductivity) self.average_bulk_mobile_defect_density = sum( bulk_mobile_defect_grid.delta_x * bulk_mobile_defect_density) / sum( bulk_mobile_defect_grid.delta_x) bulk_perpendicular = sum(bulk_mobile_defect_conductivity / bulk_mobile_defect_grid.delta_x) space_charge_parallel = sum(mobile_defect_conductivity / space_charge_region_grid.delta_x) bulk_parallel = sum(bulk_mobile_defect_grid.delta_x / bulk_mobile_defect_conductivity) perpendicular_conductivity_ratio = 1 / ( space_charge_perpendicular * bulk_perpendicular) parallel_conductivity_ratio = space_charge_parallel * bulk_parallel else: perpendicular_conductivity_ratio = 0.0 parallel_conductivity_ratio = 0.0 # self.depletion_factor = 1 - ( mobile_defect_density / average_bulk ) return perpendicular_conductivity_ratio, parallel_conductivity_ratio
def calculate_mobile_defect_conductivities( self, pos_or_neg_scr: str, scr_limit: float, species: str, mobility_scaling: bool = False) -> Tuple[float, float]: """Calculate the conductivity ratio between the space charge region and the bulk both perpendicular and parallel to the grain boundary. A `SetOfSites` object is created for the sites in the space charge region, and the defect distributions calculated. The width of the space charge region is calculated and a bulk region of the same width is defined. A SetOfSites object for the bulk region is created and the defect distributions calculated. Taking each site as a resistor in series or parallel respectively, the conductivity is calculated and the ratio between the space charge region and the bulk is taken. Args: pos_or_neg_scr (str): 'positive' - for a positive space charge potential. 'negative' - for a negative space charge potential. scr_limit (float): The minimum electrostatic potential that the electrostatic potential must exceed to be included in the space charge region. species (str): The species for which the conductivity is being calculated. mobility_scaling (bool): For particles on a lattice which only interact through volume exclusion, the mobility exhibits a blocking term. True if the blocking term is to be included, False if the blocking term is not to be included. Default = False. Returns: float: The perpendicular conductivity ratio. The conductivity ratio between the bulk and the space charge region perpendicular to the grain boundary. float: The parallel conductivity ratio. The conductivity ratio between the bulk and the space charge region parallel to the grain boundary. """ # TODO: Needs refactoring. # TODO: Updated copy of this function from Jacob. space_charge_region = self.create_space_charge_region( self.subgrids[species], pos_or_neg_scr, scr_limit) space_charge_region_limits = self.calculate_offset( self.subgrids[species], np.min(space_charge_region), np.max(space_charge_region)) space_charge_region_sites = self.create_subregion_sites( self.subgrids[species], np.min(space_charge_region), np.max(space_charge_region)) for site in space_charge_region_sites: charge = site.defects[0].valence mobilities = site.defects[0].mobility space_charge_region_grid = Grid.from_set_of_sites( space_charge_region_sites, space_charge_region_limits, space_charge_region_limits, self.grid.b, self.grid.c) space_charge_region_width = space_charge_region_grid.x[ -1] - space_charge_region_grid.x[0] mobile_defect_density = self.subgrids[ species].set_of_sites.subgrid_calculate_defect_density( self.subgrids[species], self.grid, self.phi, self.temp) space_charge_region_mobile_defect_mf = space_charge_region_sites.calculate_probabilities( space_charge_region_grid, self.phi, self.temp) space_charge_region_mobile_defect_density = space_charge_region_sites.subgrid_calculate_defect_density( space_charge_region_grid, self.grid, self.phi, self.temp) # TODO: According to Jacob this only scales the mobility of space-charge region but not the # TODO: bulk region, or vice-versa depending on whether it is switched on or not? if mobility_scaling: mobile_defect_conductivity = space_charge_region_mobile_defect_density * ( 1 - space_charge_region_mobile_defect_mf) * charge * mobilities else: mobile_defect_conductivity = space_charge_region_mobile_defect_density * charge * mobilities # TODO: This example bulk region can overlap with the space-charge region for cells that are similar lengths to the space-charge width, and will give spurious results. # TODO: Possible solution: We can directly compute the bulk resistivity from the bulk site density, without having to query the space-charge model. # TODO: Jacob to share his working code for this from his solver. bulk_x_max = self.bulk_x_min + space_charge_region_width min_bulk_index, max_bulk_index = self.find_index( self.subgrids[species], self.bulk_x_min, bulk_x_max) self.bulk_limits = self.calculate_offset(self.subgrids[species], self.bulk_x_min, bulk_x_max) bulk_mobile_defect_sites = self.create_subregion_sites( self.subgrids[species], self.bulk_x_min, bulk_x_max) bulk_mobile_defect_grid = Grid.from_set_of_sites( bulk_mobile_defect_sites, self.bulk_limits, self.bulk_limits, self.grid.b, self.grid.c) bulk_mobile_defect_density = bulk_mobile_defect_grid.set_of_sites.subgrid_calculate_defect_density( bulk_mobile_defect_grid, self.grid, self.phi, self.temp) bulk_region_mobile_defect_mf = bulk_mobile_defect_sites.calculate_probabilities( bulk_mobile_defect_grid, self.phi, self.temp) # TODO: According to Jacob this only scales the mobility of space-charge region but not the # TODO: bulk region, or vice-versa depending on whether it is switched on or not? if mobility_scaling: bulk_mobile_defect_conductivity = bulk_mobile_defect_density * charge * mobilities else: bulk_mobile_defect_conductivity = bulk_mobile_defect_density * charge * mobilities * ( 1 - bulk_region_mobile_defect_mf) space_charge_array = np.column_stack( (mobile_defect_conductivity, space_charge_region_grid.x)) bulk_array = np.column_stack( (bulk_mobile_defect_conductivity, bulk_mobile_defect_grid.x)) # TODO: This uses an incorrect definition of the perpendicular resistivity!!! # TODO: Jacob has fixed this!! # TODO: Should be refactored into its own function. if mobilities != 0.0: space_charge_perpendicular = sum(space_charge_region_grid.delta_x / mobile_defect_conductivity) self.average_bulk_mobile_defect_density = sum( bulk_mobile_defect_grid.delta_x * bulk_mobile_defect_density) / sum( bulk_mobile_defect_grid.delta_x) bulk_perpendicular = sum(bulk_mobile_defect_conductivity / bulk_mobile_defect_grid.delta_x) space_charge_parallel = sum(mobile_defect_conductivity / space_charge_region_grid.delta_x) bulk_parallel = sum(bulk_mobile_defect_grid.delta_x / bulk_mobile_defect_conductivity) perpendicular_conductivity_ratio = 1 / ( space_charge_perpendicular * bulk_perpendicular) parallel_conductivity_ratio = space_charge_parallel * bulk_parallel else: perpendicular_conductivity_ratio = 0.0 parallel_conductivity_ratio = 0.0 # self.depletion_factor = 1 - (mobile_defect_density / average_bulk) return perpendicular_conductivity_ratio, parallel_conductivity_ratio