class ApertureTEAPOT(NodeTEAPOT): """ Aperture TEAPOT element. """ def __init__(self, name = "aperture no name"): """ Constructor. Creates the aperutre element. """ NodeTEAPOT.__init__(self,name) self.setType("aperture") self.addParam("aperture", []) self.addParam("apertype", 0.0) def initialize(self): shape = self.getParam("apertype") dim = self.getParam("aperture") if len(dim) > 0: if shape == 1: self.aperture = Aperture(shape, dim[0], 0.0, 0.0, 0.0) if shape == 2: self.aperture = Aperture(shape, dim[0], dim[1], 0.0, 0.0) if shape == 3: self.aperture = Aperture(shape, dim[0], dim[1], 0.0, 0.0) def track(self, paramsDict): """ The aperture class implementation of the ApertueNode. """ bunch = paramsDict["bunch"] lostbunch = paramsDict["lostbunch"] self.aperture.checkBunch(bunch,lostbunch)
class ApertureTEAPOT(NodeTEAPOT): """ Aperture TEAPOT element. """ def __init__(self, name="aperture no name"): """ Constructor. Creates the aperutre element. """ NodeTEAPOT.__init__(self, name) self.setType("aperture") self.addParam("aperture", []) self.addParam("apertype", 0.0) def initialize(self): shape = self.getParam("apertype") dim = self.getParam("aperture") if len(dim) > 0: if shape == 1: self.aperture = Aperture(shape, dim[0], 0.0, 0.0, 0.0) if shape == 2: self.aperture = Aperture(shape, dim[0], dim[1], 0.0, 0.0) if shape == 3: self.aperture = Aperture(shape, dim[0], dim[1], 0.0, 0.0) def track(self, paramsDict): """ The aperture class implementation of the ApertueNode. """ bunch = paramsDict["bunch"] lostbunch = paramsDict["lostbunch"] self.aperture.checkBunch(bunch, lostbunch)
def __init__(self, aperture='slit', mass_profile='power_law', light_profile='Hernquist', anisotropy_type='r_ani', psf_fwhm=0.7, kwargs_cosmo={ 'D_d': 1000, 'D_s': 2000, 'D_ds': 500 }): """ initializes the observation condition and masks :param aperture_type: string :param psf_fwhm: float """ self._mass_profile = mass_profile self._fwhm = psf_fwhm self._kwargs_cosmo = kwargs_cosmo self.lightProfile = LightProfile_old(light_profile) self.aperture = Aperture(aperture) self.anisotropy = Anisotropy(anisotropy_type) self.FWHM = psf_fwhm self.jeans_solver = Jeans_solver(kwargs_cosmo, mass_profile, light_profile, anisotropy_type)
def __init__(self, shape, a, b, pos = 0., c = 0., d = 0., name = "aperture"): BaseLinacNode.__init__(self,name) self.shape = shape self.a = a self.b = b self.c = c self.d = d self.aperture = Aperture(self.shape, self.a, self.b, self.c, self.d, pos) self.setPosition(pos)
def initialize(self): shape = self.getParam("apertype") dim = self.getParam("aperture") if len(dim) > 0: if shape == 1: self.aperture = Aperture(shape, dim[0], 0.0, 0.0, 0.0) if shape == 2: self.aperture = Aperture(shape, dim[0], dim[1], 0.0, 0.0) if shape == 3: self.aperture = Aperture(shape, dim[0], dim[1], 0.0, 0.0)
def __init__(self, gb): self.gb = gb # -- flow -- # self.discr_flow = Flow(gb) shape = self.discr_flow.shape() self.flux_pressure = np.zeros(shape) # -- temperature -- # self.discr_temperature = Heat(gb) shape = self.discr_temperature.shape() self.temperature = np.zeros(shape) self.temperature_old = np.zeros(shape) # -- solute and precipitate -- # self.discr_solute_advection_diffusion = Transport(gb) self.discr_solute_precipitate_reaction = Reaction(gb) shape = self.discr_solute_advection_diffusion.shape() self.solute = np.zeros(shape) self.precipitate = np.zeros(shape) self.solute_old = np.zeros(shape) self.precipitate_old = np.zeros(shape) # -- porosity -- # self.discr_porosity = Porosity(gb) shape = self.discr_porosity.shape() self.porosity = np.zeros(shape) self.porosity_old = np.zeros(shape) self.porosity_star = np.zeros(shape) # -- aperture -- # self.discr_aperture = Aperture(gb) shape = self.discr_aperture.shape() self.aperture = np.zeros(shape) self.aperture_old = np.zeros(shape) self.aperture_star = np.zeros(shape) # -- composite variables -- # self.porosity_aperture_times_solute = np.zeros(shape) self.porosity_aperture_times_precipitate = np.zeros(shape)
def __init__(self, shape, a, b, pos = 0., c = 0., d = 0., name = "aperture"): BaseLinacNode.__init__(self,name) self.shape = shape self.a = a self.b = b self.c = c self.d = d self.pos = pos self.aperture = Aperture(self.shape, self.a, self.b, self.c, self.d, self.pos)
def __init__(self, a, b, pos = 0, c = 0, d = 0, name = "aperture"): DriftTEAPOT.__init__(self,name) self.shape = 3 self.a = a self.b = b self.c = c self.d = d self.pos = pos self.Aperture = Aperture(self.shape, self.a, self.b, self.c, self.d, self.pos)
class RectangleApertureNode(DriftTEAPOT): def __init__(self, a, b, pos = 0, c = 0, d = 0, name = "aperture"): DriftTEAPOT.__init__(self,name) self.shape = 3 self.a = a self.b = b self.c = c self.d = d self.pos = pos self.Aperture = Aperture(self.shape, self.a, self.b, self.c, self.d, self.pos) def track(self, paramsDict): bunch = paramsDict["bunch"] lostbunch = paramsDict["lostbunch"] self.Aperture.checkBunch(bunch, lostbunch) def setPosition(self,pos): self.pos = pos self.Aperture.setPosition(self.pos)
class LinacApertureNode(BaseLinacNode): """ The aperture classes removes particles from bunch and places them in the lostbunch if their coordinates are not inside the aperture: The shape variable could be: 1 is circle (a is a radius) 2 is elipse (a and b are a half-axises) 3 is rectangle (a and b are a half-horizontal and vertical sizes) c and d parameters are x and y offsets of the center """ def __init__(self, shape, a, b, pos = 0., c = 0., d = 0., name = "aperture"): BaseLinacNode.__init__(self,name) self.shape = shape self.a = a self.b = b self.c = c self.d = d self.pos = pos self.aperture = Aperture(self.shape, self.a, self.b, self.c, self.d, self.pos) def track(self, paramsDict): bunch = paramsDict["bunch"] if(paramsDict.has_key("lostbunch")): lostbunch = paramsDict["lostbunch"] self.aperture.checkBunch(bunch, lostbunch) else: self.aperture.checkBunch(bunch) def trackDesign(self, paramsDict): """ This method does nothing for the aperture case. """ pass def setPosition(self, pos): self.pos = pos self.Aperture.setPosition(self.pos) def getPosition(self): return self.pos
def __init__(self, mass_profile_list, light_profile_list, aperture_type='slit', anisotropy_model='isotropic', fwhm=0.7, kwargs_numerics={}, kwargs_cosmo={ 'D_d': 1000, 'D_s': 2000, 'D_ds': 500 }): self.massProfile = MassProfile(mass_profile_list, kwargs_cosmo) self.lightProfile = LightProfile(light_profile_list) self.aperture = Aperture(aperture_type) self.anisotropy = MamonLokasAnisotropy(anisotropy_model) self.FWHM = fwhm self.cosmo = Cosmo(kwargs_cosmo) self._num_sampling = kwargs_numerics.get('sampling_number', 1000) self._interp_grid_num = kwargs_numerics.get('interpol_grid_num', 1000) self._log_int = kwargs_numerics.get('log_integration', False) self._max_integrate = kwargs_numerics.get( 'max_integrate', 100) # maximal integration (and interpolation) in units of arcsecs
class LinacApertureNode(BaseLinacNode): """ The aperture classes removes particles from bunch and places them in the lostbunch if their coordinates are not inside the aperture: The shape variable could be: 1 is circle (a is a radius) 2 is elipse (a and b are a half-axises) 3 is rectangle (a and b are a half-horizontal and vertical sizes) c and d parameters are x and y offsets of the center """ def __init__(self, shape, a, b, pos=0., c=0., d=0., name="aperture"): BaseLinacNode.__init__(self, name) self.shape = shape self.a = a self.b = b self.c = c self.d = d self.aperture = Aperture(self.shape, self.a, self.b, self.c, self.d, pos) self.setPosition(pos) self.lost_particles_n = 0 def track(self, paramsDict): bunch = paramsDict["bunch"] n_parts = bunch.getSize() if (paramsDict.has_key("lostbunch")): lostbunch = paramsDict["lostbunch"] self.aperture.checkBunch(bunch, lostbunch) else: self.aperture.checkBunch(bunch) self.lost_particles_n = n_parts - bunch.getSize() def trackDesign(self, paramsDict): """ This method does nothing for the aperture case. """ pass def setPosition(self, pos): BaseLinacNode.setPosition(self, pos) self.aperture.setPosition(self.getPosition()) def getNumberOfLostParticles(self): return self.lost_particles_n
class Galkin(object): """ major class to compute velocity dispersion measurements given light and mass models """ def __init__(self, mass_profile_list, light_profile_list, aperture_type='slit', anisotropy_model='isotropic', fwhm=0.7, kwargs_numerics={}, kwargs_cosmo={ 'D_d': 1000, 'D_s': 2000, 'D_ds': 500 }): self.massProfile = MassProfile(mass_profile_list, kwargs_cosmo) self.lightProfile = LightProfile(light_profile_list) self.aperture = Aperture(aperture_type) self.anisotropy = MamonLokasAnisotropy(anisotropy_model) self.FWHM = fwhm self.cosmo = Cosmo(kwargs_cosmo) self._num_sampling = kwargs_numerics.get('sampling_number', 1000) self._interp_grid_num = kwargs_numerics.get('interpol_grid_num', 1000) self._log_int = kwargs_numerics.get('log_integration', False) self._max_integrate = kwargs_numerics.get( 'max_integrate', 100) # maximal integration (and interpolation) in units of arcsecs def vel_disp(self, kwargs_mass, kwargs_light, kwargs_anisotropy, kwargs_apertur, r_eff=1.): """ computes the averaged LOS velocity dispersion in the slit (convolved) :param gamma: :param phi_E: :param r_eff: :param r_ani: :param R_slit: :param FWHM: :return: """ sigma2_R_sum = 0 for i in range(0, self._num_sampling): sigma2_R = self.draw_one_sigma2(kwargs_mass, kwargs_light, kwargs_anisotropy, kwargs_apertur, r_eff=r_eff) sigma2_R_sum += sigma2_R sigma_s2_average = sigma2_R_sum / self._num_sampling # apply unit conversion from arc seconds and deflections to physical velocity disperison in (km/s) sigma_s2_average *= 2 * const.G # correcting for integral prefactor return np.sqrt(sigma_s2_average / (const.arcsec**2 * self.cosmo.D_d**2 * const.Mpc)) / 1000. # in units of km/s def draw_one_sigma2(self, kwargs_mass, kwargs_light, kwargs_anisotropy, kwargs_aperture, r_eff=1.): """ :param kwargs_mass: :param kwargs_light: :param kwargs_anisotropy: :param kwargs_aperture: :return: """ while True: R = self.lightProfile.draw_light_2d(kwargs_light, r_eff=r_eff) # draw r x, y = util.draw_xy(R) # draw projected R x_, y_ = util.displace_PSF(x, y, self.FWHM) # displace via PSF bool = self.aperture.aperture_select(x_, y_, kwargs_aperture) if bool is True: break sigma2_R = self.sigma2_R(R, kwargs_mass, kwargs_light, kwargs_anisotropy) return sigma2_R def sigma2_R(self, R, kwargs_mass, kwargs_light, kwargs_anisotropy): """ returns unweighted los velocity dispersion :param R: :param kwargs_mass: :param kwargs_light: :param kwargs_anisotropy: :return: """ I_R_sigma2 = self.I_R_simga2(R, kwargs_mass, kwargs_light, kwargs_anisotropy) I_R = self.lightProfile.light_2d(R, kwargs_light) #I_R = self.lightProfile._integrand_light(R, kwargs_light) return I_R_sigma2 / I_R def I_R_simga2(self, R, kwargs_mass, kwargs_light, kwargs_anisotropy): """ equation A15 in Mamon&Lokas 2005 as a logarithmic numerical integral modulo pre-factor 2*G :param R: :param kwargs_mass: :param kwargs_light: :param kwargs_anisotropy: :return: """ if self._log_int is True: min_log = np.log10(R + 0.0001) max_log = np.log10(self._max_integrate) r_array = np.logspace(min_log, max_log, self._interp_grid_num) dlog_r = (np.log10(r_array[1]) - np.log10(r_array[0])) * np.log(10) IR_sigma2_dr = self._integrand_A15( r_array, R, kwargs_mass, kwargs_light, kwargs_anisotropy) * dlog_r * r_array else: r_array = np.linspace(R + 0.0001, self._max_integrate, self._interp_grid_num) dr = r_array[1] - r_array[0] IR_sigma2_dr = self._integrand_A15( r_array, R, kwargs_mass, kwargs_light, kwargs_anisotropy) * dr IR_sigma2 = np.sum(IR_sigma2_dr) return IR_sigma2 def _integrand_A15(self, r, R, kwargs_mass, kwargs_light, kwargs_anisotropy): """ integrand of A15 (in log space) :param r: :param kwargs_mass: :param kwargs_light: :param kwargs_anisotropy: :return: """ k_r = self.anisotropy.K(r, R, kwargs_anisotropy) l_r = self.lightProfile.light_3d_interp(r, kwargs_light) m_r = self.massProfile.mass_3d_interp(r, kwargs_mass) out = k_r * l_r * m_r / r return out
class GalKin_old(object): """ master class for all computations """ def __init__(self, aperture='slit', mass_profile='power_law', light_profile='Hernquist', anisotropy_type='r_ani', psf_fwhm=0.7, kwargs_cosmo={ 'D_d': 1000, 'D_s': 2000, 'D_ds': 500 }): """ initializes the observation condition and masks :param aperture_type: string :param psf_fwhm: float """ self._mass_profile = mass_profile self._fwhm = psf_fwhm self._kwargs_cosmo = kwargs_cosmo self.lightProfile = LightProfile_old(light_profile) self.aperture = Aperture(aperture) self.anisotropy = Anisotropy(anisotropy_type) self.FWHM = psf_fwhm self.jeans_solver = Jeans_solver(kwargs_cosmo, mass_profile, light_profile, anisotropy_type) def vel_disp(self, kwargs_profile, kwargs_aperture, kwargs_light, kwargs_anisotropy, num=1000): """ computes the averaged LOS velocity dispersion in the slit (convolved) :param gamma: :param phi_E: :param r_eff: :param r_ani: :param R_slit: :param FWHM: :return: """ sigma_s2_sum = 0 for i in range(0, num): sigma_s2_draw = self._vel_disp_one(kwargs_profile, kwargs_aperture, kwargs_light, kwargs_anisotropy) sigma_s2_sum += sigma_s2_draw sigma_s2_average = sigma_s2_sum / num return np.sqrt(sigma_s2_average) def _vel_disp_one(self, kwargs_profile, kwargs_aperture, kwargs_light, kwargs_anisotropy): """ computes one realisation of the velocity dispersion realized in the slit :param gamma: :param rho0_r0_gamma: :param r_eff: :param r_ani: :param R_slit: :param dR_slit: :param FWHM: :return: """ while True: r = self.lightProfile.draw_light(kwargs_light) # draw r R, x, y = util.R_r(r) # draw projected R x_, y_ = util.displace_PSF(x, y, self.FWHM) # displace via PSF bool = self.aperture.aperture_select(x_, y_, kwargs_aperture) if bool is True: break sigma_s2 = self.sigma_s2(r, R, kwargs_profile, kwargs_anisotropy, kwargs_light) return sigma_s2 def sigma_s2(self, r, R, kwargs_profile, kwargs_anisotropy, kwargs_light): """ projected velocity dispersion :param r: :param R: :param r_ani: :param a: :param gamma: :param phi_E: :return: """ beta = self.anisotropy.beta_r(r, kwargs_anisotropy) return (1 - beta * R**2 / r**2) * self.sigma_r2( r, kwargs_profile, kwargs_anisotropy, kwargs_light) def sigma_r2(self, r, kwargs_profile, kwargs_anisotropy, kwargs_light): """ computes radial velocity dispersion at radius r (solving the Jeans equation :param r: :return: """ return self.jeans_solver.sigma_r2(r, kwargs_profile, kwargs_anisotropy, kwargs_light)
if __name__ == "__main__": def slowly_rotate(cls): old_update = cls.update def update(obj, dt): if old_update is not None: old_update(obj, dt) obj.orientation[:] += np.array([20, 50, 90]) * dt cls.update = update # cube = Cube() # cube.position[2] = -5. # torus = Torus(1, 0.3, 50, 30) # torus.position[2] = -4. aperture = Aperture() slowly_rotate(Aperture) aperture.position[2] = -10 aperture.size[:] = [2., 2., 2.] aperture.hole.size[:2] = [1., 1.] aperture.lbox.size[0] = 3. aperture.tbox.size[1] = 2.5 scene = Scene() scene._polygons = {'aperture': aperture} scene.exec_()
class Scheme(object): # ------------------------------------------------------------------------------# def __init__(self, gb): self.gb = gb # -- flow -- # self.discr_flow = Flow(gb) shape = self.discr_flow.shape() self.flux_pressure = np.zeros(shape) # -- temperature -- # self.discr_temperature = Heat(gb) shape = self.discr_temperature.shape() self.temperature = np.zeros(shape) self.temperature_old = np.zeros(shape) # -- solute and precipitate -- # self.discr_solute_advection_diffusion = Transport(gb) self.discr_solute_precipitate_reaction = Reaction(gb) shape = self.discr_solute_advection_diffusion.shape() self.solute = np.zeros(shape) self.precipitate = np.zeros(shape) self.solute_old = np.zeros(shape) self.precipitate_old = np.zeros(shape) # -- porosity -- # self.discr_porosity = Porosity(gb) shape = self.discr_porosity.shape() self.porosity = np.zeros(shape) self.porosity_old = np.zeros(shape) self.porosity_star = np.zeros(shape) # -- aperture -- # self.discr_aperture = Aperture(gb) shape = self.discr_aperture.shape() self.aperture = np.zeros(shape) self.aperture_old = np.zeros(shape) self.aperture_star = np.zeros(shape) # -- composite variables -- # self.porosity_aperture_times_solute = np.zeros(shape) self.porosity_aperture_times_precipitate = np.zeros(shape) # ------------------------------------------------------------------------------# def compute_flow(self): A, b = self.discr_flow.matrix_rhs() return sps.linalg.spsolve(A, b) # ------------------------------------------------------------------------------# def compute_temperature(self, porosity_star, aperture_star): # compute the matrices A, M, b = self.discr_temperature.matrix_rhs() # compute the effective thermal capacity, keeping in mind that the fracture # contains only water rc_w = self.discr_temperature.data["rc_w"] rc_s = self.discr_temperature.data["rc_s"] c_star = rc_w * (porosity_star + aperture_star) + rc_s * (1 - porosity_star) c_old = rc_w * (self.porosity_old + self.aperture_old) + rc_s * (1 - self.porosity_old) # the mass term which considers the contribution from the effective thermal capacity M_star = M * sps.diags(c_star, 0) M_old = M * sps.diags(c_old, 0) # compute the new temperature return sps.linalg.spsolve(M_star + A, M_old * self.temperature_old + b) # ------------------------------------------------------------------------------# def compute_solute_precipitate_advection_diffusion(self, porosity_star, aperture_star): # compute the matrices A, M, b = self.discr_solute_advection_diffusion.matrix_rhs() # the mass term which considers both the porosity and aperture contribution M_star = M * sps.diags(porosity_star + aperture_star, 0) M_old = M * sps.diags(self.porosity_old + self.aperture_old, 0) # compute the new solute return sps.linalg.spsolve(M_star + A, M_old * self.solute_old + b) # ------------------------------------------------------------------------------# def compute_solute_precipitate_rection(self, solute_half, precipitate_half): # the dof associated to the porous media and fractures, are the first dof = self.gb.num_cells() # temporary solution vectors solute = np.zeros(self.discr_solute_advection_diffusion.shape()) precipitate = np.zeros(self.discr_solute_advection_diffusion.shape()) # compute the new solute and precipitate solute[:dof], precipitate[:dof] = self.discr_solute_precipitate_reaction.step( solute_half[:dof], precipitate_half[:dof], self.temperature[:dof]) return solute, precipitate # ------------------------------------------------------------------------------# def set_old_variables(self): self.temperature_old = self.temperature.copy() self.solute_old = self.solute.copy() self.precipitate_old = self.precipitate.copy() self.porosity_old = self.porosity.copy() self.aperture_old = self.aperture.copy() # ------------------------------------------------------------------------------# def set_data(self, param): # set the initial condition assembler = self.discr_solute_advection_diffusion.assembler dof = np.cumsum(np.append(0, np.asarray(assembler.full_dof))) for (g, _), bi in assembler.block_dof.items(): #g = pair[0] if isinstance(g, pp.Grid): dof_loc = slice(dof[bi], dof[bi+1]) data = param["temperature"]["initial"] self.temperature[dof_loc] = data(g, param, param["tol"]) data = param["solute_advection_diffusion"]["initial_solute"] self.solute[dof_loc] = data(g, param, param["tol"]) data = param["solute_advection_diffusion"]["initial_precipitate"] self.precipitate[dof_loc] = data(g, param, param["tol"]) data = param["porosity"]["initial"] self.porosity[dof_loc] = data(g, param, param["tol"]) data = param["aperture"]["initial"] self.aperture[dof_loc] = data(g, param, param["tol"]) # set the old variables self.set_old_variables() # save the initial porosity and aperture self.discr_porosity.extract(self.porosity, "porosity_initial") self.discr_aperture.extract(self.aperture, "aperture_initial") # extract the initialized variables, useful for setting the data self.extract() # set now the data for each scheme self.discr_flow.set_data(param["flow"], param["time"]) self.discr_temperature.set_data(param["temperature"], param["time"]) self.discr_solute_advection_diffusion.set_data(param["solute_advection_diffusion"], param["time"]) self.discr_solute_precipitate_reaction.set_data(param["solute_precipitate_reaction"], param["time"]) self.discr_porosity.set_data(param["porosity"]) self.discr_aperture.set_data(param["aperture"]) # ------------------------------------------------------------------------------# def extract(self): self.discr_flow.extract(self.flux_pressure) self.discr_temperature.extract(self.temperature, "temperature") self.discr_solute_advection_diffusion.extract(self.solute, "solute") self.discr_solute_advection_diffusion.extract(self.precipitate, "precipitate") self.discr_porosity.extract(self.porosity, "porosity") self.discr_porosity.extract(self.porosity_old, "porosity_old") self.discr_aperture.extract(self.aperture, "aperture") self.discr_aperture.extract(self.aperture_old, "aperture_old") self.discr_solute_advection_diffusion.extract(self.porosity_aperture_times_solute, "porosity_aperture_times_solute") self.discr_solute_advection_diffusion.extract(self.porosity_aperture_times_precipitate, "porosity_aperture_times_precipitate") # ------------------------------------------------------------------------------# def vars_to_save(self): name = ["solute", "precipitate", "porosity", "aperture", "temperature"] name += ["porosity_aperture_times_solute", "porosity_aperture_times_precipitate"] return name + [self.discr_flow.pressure, self.discr_flow.P0_flux] # ------------------------------------------------------------------------------# def one_step_splitting_scheme(self): # the dof associated to the porous media and fractures, are the first dof = slice(0, self.gb.num_cells()) # POINT 1) extrapolate the precipitate to get a better estimate of porosity precipitate_star = 2*self.precipitate - self.precipitate_old # POINT 2) compute the porosity and aperture star porosity_star = self.discr_porosity.step(self.porosity_old, precipitate_star, self.precipitate_old) self.discr_porosity.extract(porosity_star, "porosity_star") aperture_star = self.discr_aperture.step(self.aperture_old, precipitate_star, self.precipitate_old) self.discr_aperture.extract(aperture_star, "aperture_star") # -- DO THE FLOW PART -- # # POINT 3) update the data from the previous time step self.discr_flow.update_data() # POINT 4) solve the flow part self.flux_pressure = self.compute_flow() self.discr_flow.extract(self.flux_pressure) # -- DO THE HEAT PART -- # # POINT 5) set the flux and update the data from the previous time step self.discr_temperature.set_flux(self.discr_flow.flux, self.discr_flow.mortar) self.discr_temperature.update_data() # POINT 5) solve the temperature part self.temperature = self.compute_temperature(porosity_star, aperture_star) # -- DO THE TRANSPORT PART -- # # set the flux and update the data from the previous time step self.discr_solute_advection_diffusion.set_flux(self.discr_flow.flux, self.discr_flow.mortar) self.discr_solute_advection_diffusion.update_data() # POINT 6) solve the advection and diffusion part to get the intermediate solute solution solute_half = self.compute_solute_precipitate_advection_diffusion(porosity_star, aperture_star) # POINT 7) Since in the advection-diffusion step we have accounted for porosity changes using # phi_star, the new solute concentration accounts for the change in pore volume, thus, the # precipitate needs to be updated accordingly factor = np.zeros(self.porosity_old.size) factor[dof] = (self.porosity_old[dof] + self.aperture_old[dof]) / (porosity_star[dof] + aperture_star[dof]) precipitate_half = self.precipitate_old * factor # POINT 8) solve the reaction part solute_star_star, precipitate_star_star = self.compute_solute_precipitate_rection(solute_half, precipitate_half) # -- DO THE POROSITY PART -- # # POINT 9) solve the porosity and aperture part with the true concentration of precipitate self.porosity = self.discr_porosity.step(self.porosity, precipitate_star_star, self.precipitate_old) self.aperture = self.discr_aperture.step(self.aperture, precipitate_star_star, self.precipitate_old) # POINT 10) finally, we correct the concentrations to account for the difference between the extrapolated # and "true" new porosity to ensure mass conservation factor = np.zeros(self.porosity_old.size) factor[dof] = (porosity_star[dof] + aperture_star[dof]) / (self.porosity[dof] + self.aperture[dof]) self.solute = solute_star_star * factor self.precipitate = precipitate_star_star * factor # set the old variables self.set_old_variables() # compute composite variables factor = self.porosity + self.aperture self.porosity_aperture_times_solute = factor * self.solute self.porosity_aperture_times_precipitate = factor * self.precipitate # extract all the variables, useful for exporting self.extract()