def _assign_data(self, u_bot, u_top): """ u_bot and u_top are the u_h in the neighbouring cells on the two sides of the fracture. Ordering is hard-coded according to the grids defined in _make_grid """ u_h = np.zeros(self.nd * self.g_h.num_cells) if self.nd == 3: u_h[0:6] = u_bot.ravel(order="f") u_h[12:18] = u_top.ravel(order="f") else: u_h[2:6] = u_bot.ravel(order="f") u_h[10:14] = u_top.ravel(order="f") # Project to interface e = (self.g_h, self.g_l) d_j = self.gb.edge_props(e) mg = d_j["mortar_grid"] trace = np.abs(pp.fvutils.vector_divergence(self.g_h)).T u_j = mg.primary_to_mortar_avg(nd=self.nd) * trace * u_h pp.set_iterate(d_j, {self.model.mortar_displacement_variable: u_j}) # Parameters used by the propagation class for g, d in self.gb: if g.dim == self.nd: param = {"shear_modulus": self.mu, "poisson_ratio": self.poisson} else: param = {"SIFs_critical": np.ones((self.nd, g.num_faces))} pp.initialize_data(g, d, self.model.mechanics_parameter_key, param)
def initial_condition(self) -> None: """ In addition to the values set by the parent class, we set initial value for the temperature variable, and a previous iterate value for the scalar value. The latter is used for computation of Darcy fluxes, needed for the advective term of the energy equation. """ super().initial_condition() for g, d in self.gb: # Initial value for the scalar variable. cell_zeros = np.zeros(g.num_cells) state = {self.temperature_variable: cell_zeros} iterate = {self.scalar_variable: cell_zeros} # For initial flux d[pp.STATE].update(state) pp.set_iterate(d, iterate) for _, d in self.gb.edges(): mg = d["mortar_grid"] cell_zeros = np.zeros(mg.num_cells) state = { self.mortar_temperature_variable: cell_zeros, self.mortar_temperature_advection_variable: cell_zeros, } iterate = {self.mortar_scalar_variable: cell_zeros} d[pp.STATE].update(state) pp.set_iterate(d, iterate)
def initial_condition(self) -> None: """ Initial value for the Darcy fluxes. TODO: Add to THM. """ for g, d in self.gb: d[pp.PARAMETERS] = pp.Parameters() d[pp.PARAMETERS].update_dictionaries( [self.mechanics_parameter_key, self.scalar_parameter_key,] ) self.update_all_apertures(to_iterate=False) self.update_all_apertures() super().initial_condition() for g, d in self.gb: d[pp.STATE]["cell_centers"] = g.cell_centers.copy() p0 = self.initial_scalar(g) state = { self.scalar_variable: p0, "u_exp_0": np.zeros(g.num_cells), "aperture_0": self.aperture(g) * self.length_scale, } iterate = { self.scalar_variable: p0, } # For initial flux pp.set_state(d, state) pp.set_iterate(d, iterate)
def test_add_empty_iterate(self, empty_dict): """ Add an empty iterate dictionary """ d = empty_dict pp.set_iterate(d) assert pp.STATE in d assert pp.ITERATE in d[pp.STATE]
def _initial_condition(self) -> None: """ In addition to the values set by the parent class, we set initial value for the temperature variable, and a previous iterate value for the scalar value. The latter is used for computation of Darcy fluxes, needed for the advective term of the energy equation. The Darcy flux parameter is also initialized. """ super()._initial_condition() for g, d in self.gb: # Initial value for the scalar variable. cell_zeros = np.zeros(g.num_cells) state = {self.temperature_variable: cell_zeros} iterate = {self.scalar_variable: cell_zeros} # For initial flux pp.set_state(d, state) pp.set_iterate(d, iterate) # Initial Darcy fluxes for advective flux. pp.initialize_data( g, d, self.temperature_parameter_key, { "darcy_flux": np.zeros(g.num_faces), }, ) for e, d in self.gb.edges(): mg = d["mortar_grid"] cell_zeros = np.zeros(mg.num_cells) state = { self.mortar_temperature_variable: cell_zeros, self.mortar_temperature_advection_variable: cell_zeros, } iterate = {self.mortar_scalar_variable: cell_zeros} pp.set_state(d, state) pp.set_iterate(d, iterate) # Initial Darcy fluxes for advective flux. d = pp.initialize_data( e, d, self.temperature_parameter_key, { "darcy_flux": np.zeros(mg.num_cells), }, )
def test_add_iterate_twice_and_state(self, empty_dict): """Add two state dictionaries. The existing foo value should be overwritten, while bar should be kept. Setting values in pp.STATE should not affect the iterate values. """ d = empty_dict d1 = {"foo": 1, "bar": 2} d2 = {"foo": 3, "spam": 4} pp.set_iterate(d, d1) pp.set_iterate(d, d2) pp.set_state(d, {"foo": 5}) for key, val in zip(["foo", "bar", "spam"], [3, 2, 4]): assert key in d[pp.STATE][pp.ITERATE] assert d[pp.STATE][pp.ITERATE][key] == val assert d[pp.STATE]["foo"] == 5
def _initial_condition(self): """Set initial guess for the variables. The displacement is set to zero in the Nd-domain, and at the fracture interfaces The displacement jump is thereby also zero. The contact pressure is set to zero in the tangential direction, and -1 (that is, in contact) in the normal direction. """ for g, d in self.gb: if g.dim == self._Nd: # Initialize displacement variable state = { self.displacement_variable: np.zeros(g.num_cells * self._Nd) } elif g.dim == self._Nd - 1: # Initialize contact variable traction = np.vstack( (np.zeros((g.dim, g.num_cells)), -1 * np.ones(g.num_cells))).ravel(order="F") state = { self.contact_traction_variable: traction, } pp.set_iterate(d, {self.contact_traction_variable: traction}) else: state = {} pp.set_state(d, state) for _, d in self.gb.edges(): mg = d["mortar_grid"] if mg.dim == self._Nd - 1: size = mg.num_cells * self._Nd state = {self.mortar_displacement_variable: np.zeros(size)} iterate = {self.mortar_displacement_variable: np.zeros(size)} pp.set_iterate(d, iterate) else: state = {} pp.set_state(d, state)
"mass_weight": phi * np.ones(g.num_cells), } # Assing (or update) parameters if time == 0: pp.initialize_data(g, d, param_key, specified_data) else: d[pp.PARAMETERS][param_key]["bc_values"] = bc_values d[pp.PARAMETERS][param_key]["source"] = source_term #%% Set initial states for g, d in gb: cc = g.cell_centers pp.set_state(d) pp.set_iterate(d) d[pp.STATE][pressure_var] = p_ex(cc[0], cc[1], time * np.ones_like(cc[0])) d[pp.STATE][pp.ITERATE][pressure_var] = d[pp.STATE][pressure_var].copy() #%% AD variables and manager grid_list = [g for g, _ in gb] dof_manager = pp.DofManager(gb) equation_manager = pp.ad.EquationManager(gb, dof_manager) p = equation_manager.merge_variables([(g, pressure_var) for g in grid_list]) p_m = p.previous_iteration() p_n = p.previous_timestep() #%% We let the density to be a non-linear function of the pressure def rho(p): if isinstance(p, pp.ad.Ad_array): return rho_ref * pp.ad.exp(c * (p - p_ref))
def update_all_apertures(self, to_iterate=True): """ To better control the aperture computation, it is done for the entire gb by a single function call. This also allows us to ensure the fracture apertures are updated before the intersection apertures are inherited. """ gb = self.gb for g, d in gb: apertures = np.ones(g.num_cells) if g.dim == (self.Nd - 1): # Initial aperture apertures *= self.initial_aperture # Reconstruct the displacement solution on the fracture g_h = gb.node_neighbors(g)[0] data_edge = gb.edge_props((g, g_h)) if pp.STATE in data_edge: u_mortar_local = self.reconstruct_local_displacement_jump( data_edge, from_iterate=to_iterate ) apertures -= u_mortar_local[-1].clip(max=0) if to_iterate: pp.set_iterate( d, {"aperture": apertures.copy(), "specific_volume": apertures.copy()}, ) else: state = { "aperture": apertures.copy(), "specific_volume": apertures.copy(), } pp.set_state(d, state) for g, d in gb: parent_apertures = [] num_parent = [] if g.dim < (self.Nd - 1): for edges in gb.edges_of_node(g): e = edges[0] g_h = e[0] if g_h == g: g_h = e[1] if g_h.dim == (self.Nd - 1): d_h = gb.node_props(g_h) if to_iterate: a_h = d_h[pp.STATE][pp.ITERATE]["aperture"] else: a_h = d_h[pp.STATE]["aperture"] a_h_face = np.abs(g_h.cell_faces) * a_h mg = gb.edge_props(e)["mortar_grid"] # Assumes g_h is master a_l = ( mg.mortar_to_slave_avg() * mg.master_to_mortar_avg() * a_h_face ) parent_apertures.append(a_l) num_parent.append(np.sum(mg.mortar_to_slave_int().A, axis=1)) else: raise ValueError("Intersection points not implemented in 3d") parent_apertures = np.array(parent_apertures) num_parents = np.sum(np.array(num_parent), axis=0) apertures = np.sum(parent_apertures, axis=0) / num_parents specific_volumes = np.power(apertures, self.Nd - g.dim) if to_iterate: pp.set_iterate( d, { "aperture": apertures.copy(), "specific_volume": specific_volumes.copy(), }, ) else: state = { "aperture": apertures.copy(), "specific_volume": specific_volumes.copy(), } pp.set_state(d, state) return apertures