def mu_kernel(free_energy, order_parameters, phi_field, mu_field, dx=1, discretization='standard'): """Reads from order parameter (phi) field and updates chemical potentials""" assert phi_field.spatial_dimensions == mu_field.spatial_dimensions dim = phi_field.spatial_dimensions chemical_potential = chemical_potentials_from_free_energy(free_energy, order_parameters) chemical_potential = substitute_laplacian_by_sum(chemical_potential, dim) chemical_potential = chemical_potential.subs({op: phi_field(i) for i, op in enumerate(order_parameters)}) return [Assignment(mu_field(i), discretize_spatial(mu_i, dx, discretization)) for i, mu_i in enumerate(chemical_potential)]
def force_kernel_using_pressure_tensor(force_field, pressure_tensor_field, extra_force=None, pbs=None, dx=1, discretization='standard'): dim = force_field.spatial_dimensions index_map = symmetric_tensor_linearization(dim) p = sp.Matrix(dim, dim, lambda i, j: pressure_tensor_field(index_map[i, j] if i < j else index_map[j, i])) f = force_from_pressure_tensor(p, pbs=pbs) if extra_force: f += extra_force return [Assignment(force_field(i), discretize_spatial(f_i, dx, discretization).expand()) for i, f_i in enumerate(f)]
def pressure_tensor_kernel_pbs(free_energy, order_parameters, phi_field, pressure_tensor_field, pbs_field, density_field, transformation_matrix=None, dx=1, discretization='standard'): dim = phi_field.spatial_dimensions p = pressure_tensor_from_free_energy(free_energy, order_parameters, dim, transformation_matrix, include_bulk=False) assert transformation_matrix is None p = p.subs({op: phi_field(i) for i, op in enumerate(order_parameters)}) index_map = symmetric_tensor_linearization(dim) eqs = [] for index, lin_index in index_map.items(): eq = Assignment(pressure_tensor_field(lin_index), discretize_spatial(p[index], dx, discretization).expand()) eqs.append(eq) rhs = pressure_tensor_bulk_sqrt_term(free_energy, order_parameters, density_field.center) pbs = Assignment(pbs_field.center, discretize_spatial(rhs, dx, discretization)) eqs.append(pbs) return eqs
def pressure_tensor_kernel(free_energy, order_parameters, phi_field, pressure_tensor_field, dx=1, discretization='standard', bulk_chemical_potential=None): dim = phi_field.spatial_dimensions p = pressure_tensor_from_free_energy(free_energy, order_parameters, dim, bulk_chemical_potential=bulk_chemical_potential) p = p.subs({op: phi_field(i) for i, op in enumerate(order_parameters)}) index_map = symmetric_tensor_linearization(dim) eqs = [] for index, lin_index in index_map.items(): eq = Assignment(pressure_tensor_field(lin_index), discretize_spatial(p[index], dx, discretization).expand()) eqs.append(eq) return eqs
def __init__(self, free_energy, order_parameters, domain_size, data_handling=None, name='pfn', hydro_dynamic_relaxation_rate=1.0, cahn_hilliard_relaxation_rates=1.0, cahn_hilliard_gammas=1, optimization=None): if optimization is None: optimization = {'openmp': False, 'target': Target.CPU} openmp = optimization.get('openmp', False) target = optimization.get('target', Target.CPU) if not hasattr(cahn_hilliard_relaxation_rates, '__len__'): cahn_hilliard_relaxation_rates = [cahn_hilliard_relaxation_rates ] * len(order_parameters) if not hasattr(cahn_hilliard_gammas, '__len__'): cahn_hilliard_gammas = [cahn_hilliard_gammas ] * len(order_parameters) if data_handling is None: data_handling = create_data_handling(domain_size, periodicity=True, parallel=False) dh = data_handling self.data_handling = dh stencil = LBStencil(Stencil.D3Q19) if dh.dim == 3 else LBStencil( Stencil.D2Q9) self.free_energy = free_energy # Data Handling kernel_parameters = { 'cpu_openmp': openmp, 'target': target, 'ghost_layers': 2 } gpu = target == Target.GPU phi_size = len(order_parameters) gl = kernel_parameters['ghost_layers'] self.phi_field = dh.add_array(f'{name}_phi', values_per_cell=phi_size, gpu=gpu, latex_name='φ', ghost_layers=gl) self.mu_field = dh.add_array(f"{name}_mu", values_per_cell=phi_size, gpu=gpu, latex_name="μ", ghost_layers=gl) self.vel_field = dh.add_array(f"{name}_u", values_per_cell=data_handling.dim, gpu=gpu, latex_name="u", ghost_layers=gl) self.force_field = dh.add_array(f"{name}_force", values_per_cell=dh.dim, gpu=gpu, latex_name="F", ghost_layers=gl) self.phi = SlicedGetterDataHandling(self.data_handling, self.phi_field.name) self.mu = SlicedGetterDataHandling(self.data_handling, self.mu_field.name) self.velocity = SlicedGetterDataHandling(self.data_handling, self.vel_field.name) self.force = SlicedGetterDataHandling(self.data_handling, self.force_field.name) self.ch_pdfs = [] for i in range(len(order_parameters)): src = dh.add_array(f"{name}_ch_src{i}", values_per_cell=stencil.Q, ghost_layers=gl) dst = dh.add_array(f"{name}_ch_dst{i}", values_per_cell=stencil.Q, ghost_layers=gl) self.ch_pdfs.append((src, dst)) self.hydro_pdfs = (dh.add_array(f"{name}_hydro_src", values_per_cell=stencil.Q, ghost_layers=gl), dh.add_array(f"{name}_hydro_dst", values_per_cell=stencil.Q, ghost_layers=gl)) # Compute Kernels mu_assignments = mu_kernel(self.free_energy, order_parameters, self.phi_field, self.mu_field) mu_assignments = [ Assignment(a.lhs, a.rhs.doit()) for a in mu_assignments ] mu_assignments = sympy_cse_on_assignment_list(mu_assignments) self.mu_kernel = create_kernel(mu_assignments, **kernel_parameters).compile() force_rhs = force_from_phi_and_mu(self.phi_field.center_vector, dim=dh.dim, mu=self.mu_field.center_vector) force_rhs = [ discretize_spatial(e, dx=1, stencil=fd_stencils_forth_order_isotropic) for e in force_rhs ] force_assignments = [ Assignment(lhs, rhs) for lhs, rhs in zip(self.force_field.center_vector, force_rhs) ] self.force_kernel = create_kernel(force_assignments, **kernel_parameters).compile() self.ch_lb_kernels = [] for i, (src, dst) in enumerate(self.ch_pdfs): ch_method = cahn_hilliard_lb_method( stencil, self.mu_field(i), relaxation_rate=cahn_hilliard_relaxation_rates[i], gamma=cahn_hilliard_gammas[i]) opt = optimization.copy() opt['symbolic_field'] = src opt['symbolic_temporary_field'] = dst kernel = create_lb_function( lb_method=ch_method, optimization=opt, velocity_input=self.vel_field.center_vector, output={'density': self.phi_field(i)}) self.ch_lb_kernels.append(kernel) opt = optimization.copy() opt['symbolic_field'] = self.hydro_pdfs[0] opt['symbolic_temporary_field'] = self.hydro_pdfs[1] self.hydro_lb_kernel = create_lb_function( stencil=stencil, relaxation_rate=hydro_dynamic_relaxation_rate, force=self.force_field.center_vector, output={'velocity': self.vel_field}, optimization=opt) # Setter Kernels self.init_kernels = [] for i in range(len(order_parameters)): ch_method = self.ch_lb_kernels[i].method init_assign = pdf_initialization_assignments( lb_method=ch_method, density=self.phi_field.center_vector[i], velocity=self.vel_field.center_vector, pdfs=self.ch_pdfs[i][0].center_vector) init_kernel = create_kernel(init_assign, **kernel_parameters).compile() self.init_kernels.append(init_kernel) init_assign = pdf_initialization_assignments( lb_method=self.hydro_lb_kernel.method, density=1, velocity=self.vel_field.center_vector, pdfs=self.hydro_pdfs[0].center_vector) self.init_kernels.append( create_kernel(init_assign, **kernel_parameters).compile()) # Sync functions self.phi_sync = dh.synchronization_function([self.phi_field.name]) self.mu_sync = dh.synchronization_function([self.mu_field.name]) self.pdf_sync = dh.synchronization_function( [self.hydro_pdfs[0].name] + [src.name for src, _ in self.ch_pdfs]) self.reset()
def force_kernel_using_mu(force_field, phi_field, mu_field, dx=1, discretization='standard'): """Computes forces using precomputed chemical potential - needs mu_kernel first""" assert mu_field.index_dimensions == 1 force = force_from_phi_and_mu(phi_field.center_vector, mu=mu_field.center_vector, dim=mu_field.spatial_dimensions) return [Assignment(force_field(i), discretize_spatial(f_i, dx, discretization)).expand() for i, f_i in enumerate(force)]
def create_model(domain_size, num_phases, coeff_a, coeff_epsilon, gabd, alpha=1, penalty_factor=0.01, simplex_projection=False): def lapl(e): return sum(Diff(Diff(e, i), i) for i in range(dh.dim)) def interfacial_chemical_potential(c): result = [] n = len(c) for i in range(n): entry = 0 for k in range(n): if i == k: continue eps = coeff_epsilon[(k, i)] if i < k else coeff_epsilon[(i, k)] entry += alpha**2 * eps**2 * (c[k] * lapl(c[i]) - c[i] * lapl(c[k])) result.append(entry) return -sp.Matrix(result) def bulk(c): result = 0 for i in range(num_phases): for j in range(i): result += (c[i]**2 * c[j]**2) / (4 * coeff_a[i, j]) for i in range(num_phases): for j in range(i): for k in range(j): result += gabd * c[i] * c[j] * c[k] return result # -------------- Data ------------------ dh = create_data_handling(domain_size, periodicity=(True, True), default_ghost_layers=2) c = dh.add_array("c", values_per_cell=num_phases) rho = dh.add_array("rho", values_per_cell=1) mu = dh.add_array("mu", values_per_cell=num_phases, latex_name="\\mu") force = dh.add_array("F", values_per_cell=dh.dim) u = dh.add_array("u", values_per_cell=dh.dim) # Distribution functions for each order parameter pdf_field = [] pdf_dst_field = [] for i in range(num_phases): pdf_field_local = dh.add_array(f"pdf_ch_{i}", values_per_cell=9) # 9 for D2Q9 pdf_dst_field_local = dh.add_array(f"pdfs_ch_{i}_dst", values_per_cell=9) pdf_field.append(pdf_field_local) pdf_dst_field.append(pdf_dst_field_local) # Distribution functions for the hydrodynamics pdf_hydro_field = dh.add_array("pdfs", values_per_cell=9) pdf_hydro_dst_field = dh.add_array("pdfs_dst", values_per_cell=9) # ------------- Compute kernels -------- c_vec = c.center_vector f_penalty = penalty_factor * (1 - sum(c_vec[i] for i in range(num_phases)))**2 f_bulk = bulk(c_vec) + f_penalty print(f_bulk) mu_eq = chemical_potentials_from_free_energy(f_bulk, order_parameters=c_vec) mu_eq += interfacial_chemical_potential(c_vec) mu_eq = [expand_diff_full(mu_i, functions=c) for mu_i in mu_eq] mu_assignments = [ Assignment(mu(i), discretize_spatial(mu_i, dx=1, stencil='isotropic')) for i, mu_i in enumerate(mu_eq) ] mu_compute_kernel = create_kernel(mu_assignments).compile() mu_discretize_substitutions = forth_order_isotropic_discretize(mu) force_rhs = force_from_phi_and_mu(order_parameters=c_vec, dim=dh.dim, mu=mu.center_vector) force_rhs = force_rhs.subs(mu_discretize_substitutions) force_assignments = [ Assignment(force(i), force_rhs[i]) for i in range(dh.dim) ] force_kernel = create_kernel(force_assignments).compile() ch_collide_kernels = [] ch_methods = [] for i in range(num_phases): ch_method = cahn_hilliard_lb_method(LBStencil(Stencil.D2Q9), mu(i), relaxation_rate=1.0, gamma=1.0) ch_methods.append(ch_method) lbm_config = LBMConfig(lb_method=ch_method, kernel_type='collide_only', density_input=c(i), velocity_input=u.center_vector, compressible=True) lbm_opt = LBMOptimisation(symbolic_field=pdf_field[i]) ch_update_rule = create_lb_update_rule(lbm_config=lbm_config, lbm_optimisation=lbm_opt) ch_assign = ch_update_rule.all_assignments ch_kernel = create_kernel(ch_assign).compile() ch_collide_kernels.append(ch_kernel) ch_stream_kernels = [] for i in range(num_phases): ch_method = ch_methods[i] lbm_config = LBMConfig(lb_method=ch_method, kernel_type='stream_pull_only', temporary_field_name=pdf_dst_field[i].name) lbm_opt = LBMOptimisation(symbolic_field=pdf_field[i]) ch_update_rule = create_lb_update_rule(lbm_config=lbm_config, lbm_optimisation=lbm_opt) ch_assign = ch_update_rule.all_assignments ch_kernel = create_kernel(ch_assign).compile() ch_stream_kernels.append(ch_kernel) # Defining the initialisation kernels for the C-H pdfs init_kernels = [] for i in range(num_phases): ch_method = ch_methods[i] init_assign = pdf_initialization_assignments( lb_method=ch_method, density=c_vec[i], velocity=(0, 0), pdfs=pdf_field[i].center_vector) init_kernel = create_kernel(init_assign).compile() init_kernels.append(init_kernel) getter_kernels = [] for i in range(num_phases): cqc = ch_methods[i].conserved_quantity_computation output_assign = cqc.output_equations_from_pdfs( pdf_field[i].center_vector, {'density': c(i)}) getter_kernel = create_kernel(output_assign).compile() getter_kernels.append(getter_kernel) lbm_config = LBMConfig(kernel_type='collide_only', relaxation_rate=1.0, force=force, compressible=True) lbm_opt = LBMOptimisation(symbolic_field=pdf_hydro_field) collide_assign = create_lb_update_rule(lbm_config=lbm_config, lbm_optimisation=lbm_opt) collide_kernel = create_kernel(collide_assign).compile() lbm_config = LBMConfig(kernel_type='stream_pull_only', temporary_field_name=pdf_hydro_dst_field.name, output={ "density": rho, "velocity": u }) lbm_opt = LBMOptimisation(symbolic_field=pdf_hydro_field) stream_assign = create_lb_update_rule(lbm_config=lbm_config, lbm_optimisation=lbm_opt) stream_kernel = create_kernel(stream_assign).compile() method_collide = collide_assign.method init_hydro_assign = pdf_initialization_assignments( lb_method=method_collide, density=rho.center, velocity=u.center_vector, pdfs=pdf_hydro_field.center_vector) init_hydro_kernel = create_kernel(init_hydro_assign).compile() output_hydro_assign = cqc.output_equations_from_pdfs( pdf_hydro_field.center_vector, { 'density': rho.center, 'velocity': u.center_vector }).all_assignments # Creating getter kernel to extract quantities getter_hydro_kernel = create_kernel( output_hydro_assign).compile() # getter kernel # Setting values of arrays dh.cpu_arrays[c.name].fill(0) dh.cpu_arrays[u.name].fill(0) dh.cpu_arrays[rho.name].fill(1) dh.cpu_arrays[mu.name].fill(0) dh.cpu_arrays[force.name].fill(0) def init(): for k in init_kernels: dh.run_kernel(k) dh.run_kernel(init_hydro_kernel) pdf_sync_fns = [] for i in range(num_phases): sync_fn = dh.synchronization_function([pdf_field[i].name]) pdf_sync_fns.append(sync_fn) hydro_sync_fn = dh.synchronization_function([pdf_hydro_field.name]) c_sync_fn = dh.synchronization_function([c.name]) mu_sync = dh.synchronization_function([mu.name]) def run(steps): for t in range(steps): # μ and P c_sync_fn() dh.run_kernel(mu_compute_kernel) mu_sync() dh.run_kernel(force_kernel) # Hydrodynamic LB dh.run_kernel(collide_kernel) # running collision kernel hydro_sync_fn() dh.run_kernel(stream_kernel) # running streaming kernel dh.swap(pdf_hydro_field.name, pdf_hydro_dst_field.name) dh.run_kernel(getter_hydro_kernel) # Cahn-Hilliard LBs for i in range(num_phases): dh.run_kernel(ch_collide_kernels[i]) pdf_sync_fns[i]() dh.run_kernel(ch_stream_kernels[i]) dh.swap(pdf_field[i].name, pdf_dst_field[i].name) dh.run_kernel(getter_kernels[i]) if simplex_projection: simplex_projection_2d(dh.cpu_arrays[c.name]) return dh.cpu_arrays[c.name][1:-1, 1:-1, :] return dh, init, run