def store_mesh(self, mesh: dolfin.Mesh, cell_domains: dolfin.MeshFunction = None, facet_domains: dolfin.MeshFunction = None) -> None: """Save the mesh, and cellfunction and facet function if provided.""" with dolfin.XDMFFile(mesh.mpi_comm(), str(self._casedir / "mesh.xdmf")) as meshfile: meshfile.write(mesh) # if cell_domains is not None: # meshfile.write(cell_domains, "cell_domains") # if facet_domains is not None: # meshfile.write(facet_domains, "facet_domains") if cell_domains is not None: with df.XDMFFile(mesh.mpi_comm(), str(self._casedir / "cell_function.xdmf")) as cf_file: cf_file.write(mesh) cf_file.write(cell_domains) if facet_domains is not None: with df.XDMFFile(mesh.mpi_comm(), str(self._casedir / "facet_function.xdmf")) as ff_file: ff_file.write(mesh) ff_file.write(facet_domains)
def readmesh(mesh_file): ''' read HDF5 or DOLFIN XML mesh ''' # TODO: exceptions, files exist? from dolfin import Mesh, MeshFunction, CellFunction, HDF5File, \ FacetFunction tmp = mesh_file.split('/')[-1].split('.') mesh_type = tmp[-1] mesh_name = '.'.join(tmp[0:-1]) if mesh_type == 'xml': mesh = Mesh(mesh_file) rank = mesh.mpi_comm().Get_rank() try: subdomains = MeshFunction("size_t", mesh, mesh_name+"_physical_region.xml") except: if rank == 0: print('no subdomain file found (%s)' % (mesh_name+"_physical_region.xml")) subdomains = CellFunction("size_t", mesh) try: boundaries = MeshFunction("size_t", mesh, mesh_name+"_facet_region.xml") except: if rank == 0: print('no boundary file found (%s)' % (mesh_name+"_physical_region.xml")) boundaries = FacetFunction("size_t", mesh) elif mesh_type == 'h5': mesh = Mesh() rank = mesh.mpi_comm().Get_rank() hdf = HDF5File(mesh.mpi_comm(), mesh_file, "r") hdf.read(mesh, "/mesh", False) subdomains = CellFunction("size_t", mesh) boundaries = FacetFunction("size_t", mesh) if hdf.has_dataset('subdomains'): hdf.read(subdomains, "/subdomains") else: if rank == 0: print('no <subdomains> datasets found in file %s' % mesh_file) if hdf.has_dataset('boundaries'): hdf.read(boundaries, "/boundaries") else: if rank == 0: print('no <boundaries> datasets found in file %s' % mesh_file) elif mesh_type in ['xdmf', 'xmf']: import sys sys.exit('XDMF not supported yet. Use HDF5 instead!') else: import sys sys.exit('mesh format not recognized. try XML (serial) or HDF5') # NOTE http://fenicsproject.org/qa/5337/importing-marked-mesh-for-parallel-use # see file xml2xdmf.py return mesh, subdomains, boundaries
def make_mesh(coordinates, cells, tdim, gdim): '''Mesh by MeshEditor from vertices and cells''' mesh = Mesh() # FEniCS 2017 try: assert mesh.mpi_comm().tompi4py().size == 1 # FEniCS 2018 except AttributeError: assert mesh.mpi_comm().size == 1 coordinates.shape = (np.prod(coordinates.shape), ) cells.shape = (np.prod(cells.shape), ) fill_mesh(coordinates, cells, tdim, gdim, mesh) return mesh
def __init__(self, mesh: df.Mesh, time: df.Constant, M_i: tp.Union[df.Expression, tp.Dict[int, df.Expression]], I_s: tp.Union[df.Expression, tp.Dict[int, df.Expression]] = None, v_: df.Function = None, cell_domains: df.MeshFunction = None, facet_domains: df.MeshFunction = None, parameters: df.Parameters = None) -> None: super().__init__(mesh, time, M_i, I_s=I_s, v_=v_, cell_domains=cell_domains, facet_domains=facet_domains, parameters=parameters) # Create variational forms self._timestep = df.Constant(self.parameters["default_timestep"]) self._lhs, self._rhs, self._prec = self.variational_forms( self._timestep) # Preassemble left-hand side (will be updated if time-step changes) self._lhs_matrix = df.assemble(self._lhs) self._rhs_vector = df.Vector(mesh.mpi_comm(), self._lhs_matrix.size(0)) self._lhs_matrix.init_vector(self._rhs_vector, 0) # Create linear solver (based on parameter choices) self._linear_solver = self._create_linear_solver()
def convert(msh_file, h5_file): '''Temporary version of convert from msh to h5''' root, _ = os.path.splitext(msh_file) assert os.path.splitext(msh_file)[1] == '.msh' assert os.path.splitext(h5_file)[1] == '.h5' # Get the xml mesh xml_file = '.'.join([root, 'xml']) subprocess.call(['dolfin-convert %s %s' % (msh_file, xml_file)], shell=True) # Success? assert os.path.exists(xml_file) mesh = Mesh(xml_file) out = HDF5File(mesh.mpi_comm(), h5_file, 'w') out.write(mesh, 'mesh') info('Mesh has %d cells' % mesh.num_cells()) info('Mesh size %g %g' % (mesh.hmin(), mesh.hmax())) outputs = [mesh] # Save ALL data as facet_functions names = ('surfaces', 'volumes') for name, region in zip(names, ('facet_region.xml', 'physical_region.xml')): r_xml_file = '_'.join([root, region]) f = MeshFunction('size_t', mesh, r_xml_file) out.write(f, name) outputs.append(f) return outputs
def convert(msh_file): '''Temporary version of convertin from msh to h5''' root, _ = os.path.splitext(msh_file) assert os.path.splitext(msh_file)[1] == '.msh' # Get the xml mesh xml_file = '.'.join([root, 'xml']) # Convert to XML convert2xml(msh_file, xml_file) # Success? assert os.path.exists(xml_file) mesh = Mesh(xml_file) h5_file = '.'.join([root, 'h5']) out = HDF5File(mesh.mpi_comm(), h5_file, 'w') out.write(mesh, 'mesh') # Save ALL data as facet_functions data_sets = ('curves', 'surfaces', 'volumes') regions = ('curve_region.xml', 'facet_region.xml', 'volume_region.xml') for data_set, region in zip(data_sets, regions): r_xml_file = '_'.join([root, region]) if os.path.exists(r_xml_file): f = MeshFunction('size_t', mesh, r_xml_file) out.write(f, data_set) # And clean os.remove(r_xml_file) # and clean os.remove(xml_file) return h5_file
def convert(msh_file, h5_file): '''Convert msh file to h5_file''' root, _ = os.path.splitext(msh_file) assert os.path.splitext(msh_file)[1] == '.msh' assert os.path.splitext(h5_file)[1] == '.h5' # Get the xml mesh xml_file = '.'.join([root, 'xml']) subprocess.call(['dolfin-convert %s %s' % (msh_file, xml_file)], shell=True) # Success? assert os.path.exists(xml_file) mesh = Mesh(xml_file) out = HDF5File(mesh.mpi_comm(), h5_file, 'w') out.write(mesh, 'mesh') for region in ('facet_region.xml', ): name, _ = region.split('_') r_xml_file = '_'.join([root, region]) f = MeshFunction('size_t', mesh, r_xml_file) out.write(f, name) # Sucess? assert os.path.exists(h5_file) return mesh
def load_mesh(filename, subdir="mesh", use_partition_from_file=False): """ Loads the mesh specified by the argument filename. """ info_cyan("Loading mesh: " + filename) mesh = Mesh() h5file = HDF5File(mesh.mpi_comm(), filename, "r") h5file.read(mesh, subdir, use_partition_from_file) h5file.close() return mesh
def make_mesh(coordinates, cells, tdim, gdim): '''Mesh by MeshEditor from vertices and cells''' mesh = Mesh() assert mesh.mpi_comm().tompi4py().size == 1 fill_mesh(coordinates.flatten(), cells.flatten(), tdim, gdim, mesh) return mesh
def load_mesh(filename): """ Loads in the mesh specified by the argument filename. """ info_cyan("Loading mesh: " + filename) mesh = Mesh() h5file = HDF5File(mesh.mpi_comm(), filename, "r") h5file.read(mesh, "mesh", False) h5file.close() return mesh
def make_mesh(coordinates, cells, tdim, gdim, mesh=None): '''Mesh by MeshEditor from vertices and cells''' if mesh is None: mesh = Mesh() assert mesh.mpi_comm().size == 1 module.fill_mesh(coordinates.flatten(), cells.flatten(), tdim, gdim, mesh) return mesh
def read_h5(h5Path): mesh = Mesh() hdf = HDF5File(mesh.mpi_comm(), h5Path, 'r') hdf.read(mesh, '/mesh', False) subdomains = MeshFunction('size_t', mesh, 3) boundaries = MeshFunction('size_t', mesh, 2) hdf.read(subdomains, '/subdomains') hdf.read(boundaries, '/boundaries') return mesh, subdomains, boundaries
def store_mesh(self, mesh: dolfin.Mesh, cell_domains: dolfin.MeshFunction = None, facet_domains: dolfin.MeshFunction = None) -> None: """Save the mesh, and cellfunction and facet function if provided.""" filename = self.casedir / Path("mesh.hdf5") with dolfin.HDF5File(mesh.mpi_comm(), str(filename), "w") as meshfile: meshfile.write(mesh, "/Mesh") if cell_domains is not None: meshfile.write(cell_domains, "/CellDomains") if facet_domains is not None: meshfile.write(facet_domains, "/FacetDomains")
def load_h5_mesh(h5_file, scale_factor=None): '''Unpack to mesh, volumes and surfaces''' mesh = Mesh() h5 = HDF5File(mesh.mpi_comm(), h5_file, 'r') h5.read(mesh, 'mesh', False) # Convert units if scale_factor is not None: assert scale_factor > 0, "Scale factor must be a positive real number!" mesh.coordinates()[:] *= scale_factor surfaces = MeshFunction('size_t', mesh, mesh.topology().dim() - 1) h5.read(surfaces, 'surfaces') volumes = MeshFunction('size_t', mesh, mesh.topology().dim()) h5.read(volumes, 'volumes') return mesh, volumes, surfaces
def as_pvd(h5_file): '''Store facet and cell function for pvd''' root, ext = os.path.splitext(h5_file) mesh = Mesh() hdf = HDF5File(mesh.mpi_comm(), h5_file, 'r') hdf.read(mesh, '/mesh', False) facet_markers = FacetFunction('size_t', mesh) hdf.read(facet_markers, '/facet_markers') cell_markers = CellFunction('size_t', mesh) hdf.read(cell_markers, '/cell_markers') File(root + 'facets' + '.pvd') << facet_markers File(root + 'volumes' + '.pvd') << cell_markers return True
def convert(msh_file, h5_file, save_mvc=False): '''Temporary version of convertin from msh to h5''' root, _ = os.path.splitext(msh_file) assert os.path.splitext(msh_file)[1] == '.msh' assert os.path.splitext(h5_file)[1] == '.h5' # Get the xml mesh xml_file = '.'.join([root, 'xml']) subprocess.call(['dolfin-convert %s %s' % (msh_file, xml_file)], shell=True) # Success? assert os.path.exists(xml_file) mesh = Mesh(xml_file) out = HDF5File(mesh.mpi_comm(), h5_file, 'w') out.write(mesh, 'mesh') print('Mesh has %d cells' % mesh.num_cells()) print('Mesh size %g %g' % (mesh.hmin(), mesh.hmax())) # Save ALL data as facet_functions names = ('surfaces', 'volumes') if not save_mvc: for name, region in zip(names, ('facet_region.xml', 'physical_region.xml')): r_xml_file = '_'.join([root, region]) f = MeshFunction('size_t', mesh, r_xml_file) print('%d %s with 1' % (sum(1 for _ in SubsetIterator(f, 1)), name)) out.write(f, name) return True for name, region in zip(names, ('facet_region.xml', 'physical_region.xml')): r_xml_file = '_'.join([root, region]) f = MeshFunction('size_t', mesh, r_xml_file) # With mesh value collection we only store nonzero tags mvc = MeshValueCollection('size_t', mesh, f.dim()) # Fill fill_mvc_from_mf(f, mvc) # And save out.write(mvc, name) return True
for (nx, dt, pres, store_step) in zip(nx_list, dt_list, pres_list, storestep_list): if comm.Get_rank() == 0: print("Starting computation with grid resolution " + str(nx)) # Compute num steps till completion num_steps = np.rint(Tend / float(dt)) # Generate mesh mesh = Mesh("./../../meshes/circle_0.xml") n = nx while n > 1: mesh = refine(mesh) n /= 2 output_field = XDMFFile(mesh.mpi_comm(), outdir + "psi_h_nx" + str(nx) + ".xdmf") # Velocity and initial condition V = VectorFunctionSpace(mesh, "DG", 3) uh = Function(V) uh.assign(Expression(("-Uh*x[1]", "Uh*x[0]"), Uh=Uh, degree=3)) psi0_expression = GaussianPulse( center=(xc, yc), sigma=float(sigma), U=[Uh, Uh], time=0.0, height=1.0, degree=3 ) # Generate particles x = RandomCircle(Point(x0, y0), r).generate([pres, pres]) s = np.zeros((len(x), 1), dtype=np.float_) # Initialize particles with position x and scalar property s at the mesh
class FBVP: def __init__(self, mesh_name, parameters, alpha_range=None): # LOAD MESH AND PARAMETERS self.parameters = parameters self.mesh_name = mesh_name if not alpha_range: self.alpha_range = parameters.alpha_range else: self.alpha_range = alpha_range mesh_path = self.parameters.get_mesh_path() self.mesh = Mesh() with XDMFFile(mesh_path) as f: f.read(self.mesh) print(f'Mesh size= {self.mesh.hmax()}') # dimension of approximation space self.dim = len(self.mesh.coordinates()) print(f'Dimension of solution space is {self.dim}') self.V = FunctionSpace(self.mesh, 'CG', 1) # CG = P1 self.coords = self.mesh.coordinates()[dof_to_vertex_map(self.V)] self.T = self.parameters.T # final time self.w = TrialFunction(self.V) self.u = TestFunction(self.V) ####################################################################### # CONTROL SET CREATION self.control_set = np.linspace(self.alpha_range[0], self.alpha_range[1], self.parameters.control_set_size) self.control_set_size = len(self.control_set) print(f'Discretized control set has size {self.control_set_size}') ####################################################################### # BOUNDARY CONDITIONS parameters.set_boundary_conditions(self.mesh) self.boundary_markers = MeshFunction('size_t', self.mesh, 1) self.boundary_markers.set_all(4) # pylint: disable=no-member for i, omega in self.parameters.omegas.items(): omega.mark(self.boundary_markers, i) self.ds = Measure('ds', domain=self.mesh, subdomain_data=self.boundary_markers) self.dirichlet_bcs = [ DirichletBC(self.V, parameters.RHS_bound[j], self.boundary_markers, j) for j in self.parameters.regions["Dirichlet"] ] # Get indices of dirichlet and robin dofs self.dirichlet_nodes_list = set() self.dirichlet_nodes_dict = {} for j in self.parameters.regions["Dirichlet"]: bc = DirichletBC(self.V, Constant(0), self.boundary_markers, j) self.dirichlet_nodes_list |= set(bc.get_boundary_values().keys()) self.dirichlet_nodes_dict[j] = list( bc.get_boundary_values().keys()) self.robin_nodes_list = set() self.robin_nodes_dict = {} for j in self.parameters.regions["Robin"]: bc = DirichletBC(self.V, Constant(0), self.boundary_markers, j) self.robin_nodes_list |= set(bc.get_boundary_values().keys()) self.robin_nodes_dict[j] = list(bc.get_boundary_values().keys()) bc = DirichletBC(self.V, Constant(0), 'on_boundary') self.boundary_nodes_list = bc.get_boundary_values().keys() self.robint_nodes_list = set() self.robint_nodes_dict = {} for j in self.parameters.regions["RobinTime"]: bc = DirichletBC(self.V, Constant(0), self.boundary_markers, j) self.robint_nodes_list |= set(bc.get_boundary_values().keys()) self.robint_nodes_dict[j] = list(bc.get_boundary_values().keys()) ####################################################################### # ASSEMBLY time_start = time.process_time() self.assemble_diagonal_matrix() # auxilliary generic diagonal matrix # used for vector*matrix multiplication of dolfin matrices self.assemble_lumpedmm() # lumped mass matrix # which serves the role of identity operator self.assemble_laplacian() # discrete laplacian self.ad_data_path = f'out/{self.parameters.experiment}' Path(self.ad_data_path).mkdir(parents=True, exist_ok=True) self.timesteps = self.parameters.get_number_of_timesteps() self.assemble_HJBe() # assembly of explicit operators self.assemble_HJBi() # assembly of implicit operators self.assemble_RHS() # assembly of forcing term print('Final time assembly complete') print(f'Assembly took {time.process_time() - time_start} seconds') print('===========================================================') def assemble_diagonal_matrix(self): print("Assembling auxilliary diagonal matrix") """ Operator assembled to get the right sparameterssity pattern (non-zero terms can only exist on diagonal) """ mesh3 = UnitIntervalMesh(self.dim) V3 = FunctionSpace(mesh3, "DG", 0) wid = TrialFunction(V3) uid = TestFunction(V3) self.diag_matrix = assemble(uid * wid * dx) self.diag_matrix.zero() def assemble_lumpedmm(self): """ Assembly lumped mass matrix - equal to 0 on robin boundary since there is no time derivative there. """ print("Assembling lumped mass matrix") mass_form = self.w * self.u * dx mass_action_form = action(mass_form, Constant(1)) self.MM_terms = assemble(mass_action_form) for n in self.robint_nodes_list: self.MM_terms[n] = 1.0 for n in self.robin_nodes_list: self.MM_terms[n] = 0.0 self.mass_matrix = assemble(mass_form) self.mass_matrix.zero() self.mass_matrix.set_diagonal(self.MM_terms) self.scipy_mass_matrix = toscipy(self.mass_matrix) def assemble_laplacian(self): print("Assembling laplacians") # laplacian discretisation self.laplacian = assemble(dot(grad(self.w), grad(self.u)) * dx) def assemble_HJBe(self, t=None): """ Assembly explicit operator for every control in the control set, then apply artificial diffusion to all of them. Note that artificial diffusion is calculated differently on the boundary nodes. """ self.explicit_matrices = np.empty(self.control_set_size, dtype=object) self.explicit_diffusion = np.empty([self.control_set_size, self.dim]) diagonal_vector = Vector(self.mesh.mpi_comm(), self.dim) global_min_timestep = float('inf') # Create explicit operator for each control in control set for k, alpha in enumerate(self.control_set): if k % 10 == 0: print(f'Assembling explicit matrix under control {k}' f' out of {self.control_set_size}') # coefficients in the interior advection_x = self.parameters.adv_x(alpha) advection_y = self.parameters.adv_y(alpha) reaction = self.parameters.lin(alpha) if t is not None: advection_x.t = t advection_y.t = t reaction.t = t # Discretize PDE (set values on boundary rows to zero) b = np.array([advection_x, advection_y]) E_interior_form = (np.dot(b, grad(self.w)) + reaction * self.w) * self.u * dx explicit_matrix = assemble(E_interior_form) set_zero_rows(explicit_matrix, self.boundary_nodes_list) # Calculate diffusion necessary to make explicit operator monotone min_diffusion = np.zeros(explicit_matrix.size(0)) for rows, row_num in getrows(explicit_matrix, self.laplacian, ignore=self.boundary_nodes_list): min_diffusion[row_num] = calc_ad(rows, row_num) self.explicit_diffusion[k] = min_diffusion discrete_diffusion = apply_ad(self.laplacian, min_diffusion) explicit_matrix += discrete_diffusion for j in self.parameters.regions['RobinTime']: self.set_directional_derivative( explicit_matrix, region=j, nodes=self.robint_nodes_dict[j], control=alpha, time=t) explicit_matrix.get_diagonal(diagonal_vector) current_min_timestep = get_min_timestep( diagonal_vector, self.MM_terms, self.dirichlet_nodes_list | self.robin_nodes_list) global_min_timestep = min(current_min_timestep, global_min_timestep) self.explicit_matrices[k] = toscipy(explicit_matrix) ####################################################################### min_timesteps = int(self.T / global_min_timestep) + 1 if not self.timesteps or self.timesteps < min_timesteps: self.timesteps = min_timesteps try: filename = (f'meshes/{self.parameters.domain}/' f'Mvalues-{self.parameters.experiment}.json') with open(filename, 'r') as f: min_timesteps_dict = json.load(f) except FileNotFoundError: min_timesteps_dict = {} min_timesteps_dict[self.parameters.mesh_name] = self.timesteps with open(filename, 'w') as f: json.dump(min_timesteps_dict, f) self.timestep_size = self.T / self.timesteps # time step size self.parameters.calculate_save_interval() self.explicit_matrices = self.timestep_size * self.explicit_matrices - \ np.repeat(self.scipy_mass_matrix, self.control_set_size) print('Checking if the explicit operators satisfy' ' monotonicity conditions') for explicit_matrix in self.explicit_matrices: explicit_check(explicit_matrix, self.dirichlet_nodes_list) def assemble_RHS(self, t=None): """Assemble right hand side of the FBVP """ print('Assembling RHS') self.forcing_terms = np.empty([self.control_set_size, self.dim]) for i, alpha in enumerate(self.control_set): rhs = self.parameters.RHSt(alpha) if t is not None: rhs.t = t # Initialise forcing term F = np.array(assemble(rhs * self.u * dx)[:]) for j in self.parameters.regions['RobinTime']: rhs = self.parameters.RHS_bound[j](alpha) if t is not None: rhs.t = t bc = DirichletBC(self.V, rhs, self.boundary_markers, j) vals = bc.get_boundary_values() F[list(vals.keys())] = list(vals.values()) for j in self.parameters.regions['Robin']: rhs = self.parameters.RHS_bound[j](alpha) if t is not None: rhs.t = t bc = DirichletBC(self.V, rhs, self.boundary_markers, j) vals = bc.get_boundary_values() F[list(vals.keys())] = list(vals.values()) self.forcing_terms[i] = self.timestep_size * F def assemble_HJBi(self, t=None): """ Assembly matrix discretizing second order terms after diffusion moved to the explicit operator is subtracted. Whenever amount of diffusion used to make some row of an explicit operator monotonic exceeds the amount of natural diffusion at that node we call it artificial diffusion. In such case, this row of the implicit operator is multiplied by zero """ print('Assembling implicit operators') self.implicit_matrices = [] remaining_diffusion = Function(self.V) max_art_dif = 0.0 max_art_dif_loc = None diffusion_matrix = self.diag_matrix.copy() for explicit_diffusion, alpha in \ zip(self.explicit_diffusion, self.control_set): diffusion = self.parameters.diffusion(alpha) if t is not None: diffusion.t = t diff = interpolate(diffusion, self.V).vector() if not np.all(diff >= 0): raise Exception("Choose non-negative diffusion") diff_vec = np.array([ diff[i] if i not in self.boundary_nodes_list else 0.0 for i in range(self.dim) ]) artificial_diffusion = explicit_diffusion - diff_vec if np.amax(artificial_diffusion) > max_art_dif: max_art_dif = np.amax(artificial_diffusion) max_art_dif_loc = self.coords[np.argmax(artificial_diffusion)] # discretise second order terms remaining_diffusion.vector()[:] = np.maximum( -artificial_diffusion, [0] * self.dim) diffusion_matrix.set_diagonal(remaining_diffusion.vector()) implicit_matrix = matmult(diffusion_matrix, self.laplacian) for j in self.parameters.regions['Robin']: self.set_directional_derivative(implicit_matrix, region=j, nodes=self.robin_nodes_dict[j], control=alpha, time=t) self.implicit_matrices.append(self.timestep_size * implicit_matrix + self.mass_matrix) self.scipy_implicit_matrices = [ toscipy(mat) for mat in self.implicit_matrices ] print('Checking if the implicit operators satisfy' ' monotonicity conditions') for implicit_matrix in self.scipy_implicit_matrices: implicit_check(implicit_matrix) with open(self.ad_data_path + '/ad.txt', 'a') as f: time_str = f' at time {t}\n' if t is not None else '\n' f.write(f'For mesh {self.mesh_name} max value of artificial' ' diffusion coefficient was' f' {max_art_dif} at {max_art_dif_loc}' + time_str) def set_directional_derivative(self, operator, region, nodes, control, time=None): if region in self.parameters.regions['Robin']: adv_x = self.parameters.robin_adv_x[region](control) adv_y = self.parameters.robin_adv_y[region](control) lin = self.parameters.robin_lin[region](control) elif region in self.parameters.regions['RobinTime']: adv_x = self.parameters.robint_adv_x[region](control) adv_y = self.parameters.robint_adv_y[region](control) lin = self.parameters.robint_lin[region](control) if time is not None: adv_x.t = time adv_y.t = time lin.t = time b = (interpolate(adv_x, self.V), interpolate(adv_y, self.V)) c = interpolate(lin, self.V) for n in nodes: # node coordinates x = self.coords[n] # evaluate advection at robin node b_x = np.array([b[0].vector()[n], b[1].vector()[n]]) # denominator used to calculate directional derivative if np.linalg.norm(b_x) > 1: lamb = 0.1 * self.mesh.hmin() / np.linalg.norm(b_x) else: lamb = 0.1 * self.mesh.hmin() # position of first node of the stencil x_prev = x - lamb * b_x # Find cell containing first node of stencil and get its # dof/vertex coordinates try: cell_ind = self.mesh.bounding_box_tree( ).compute_entity_collisions(Point(x_prev))[0] except IndexError: i = 16 while i > 2: # sometimes Fenics does not detect nodes if boundary is # parallel to the boundary advection due to rounding errors # so try different precisions just to be sure try: cell_ind = self.mesh.bounding_box_tree( ).compute_entity_collisions(Point(np.round(x_prev, i)))[0] break except IndexError: i -= 1 else: raise Exception( "Boundary advection outside tangential cone") cell_vertices = self.mesh.cells()[cell_ind] cell_dofs = vertex_to_dof_map(self.V)[cell_vertices] cell_coords = self.mesh.coordinates()[cell_vertices] # calculate weigth of each vertex in the cell (using # barycentric coordinates) A = np.vstack((cell_coords.T, np.ones(3))) rhs = np.append(x_prev, np.ones(1)) weights = np.linalg.solve(A, rhs) weights = [w if w > 1e-14 else 0 for w in weights] dof_to_weight = dict(zip(cell_dofs, weights)) # calculate directional derivative at each node using # weights to interpolate value of numerical solution at # x_prev row = operator.getrow(n) indices = row[0] data = row[1] for dof in cell_dofs: pos = np.where(indices == dof)[0][0] if dof != n: data[pos] = -dof_to_weight[dof] / lamb else: c_n = c.vector()[dof] # make sure reaction term is positive adding artificial # constant if necessary if region in self.parameters.regions['Robin']: c_n = max(c_n, min(lamb, 1E-8)) data[pos] = (1 - dof_to_weight[dof]) / lamb + c_n operator.set([data], [n], indices) operator.apply('insert')
for (nx, dt, pres, store_step) in zip(nx_list, dt_list, pres_list, storestep_list): if comm.Get_rank() == 0: print("Starting computation with grid resolution " + str(nx)) # Compute num steps till completion num_steps = np.rint(Tend / float(dt)) # Generate mesh mesh = Mesh("./../../meshes/circle_0.xml") n = nx while n > 1: mesh = refine(mesh) n /= 2 output_field = XDMFFile(mesh.mpi_comm(), outdir + "psi_h_nx" + str(nx) + ".xdmf") # Velocity and initial condition V = VectorFunctionSpace(mesh, "DG", 3) uh = Function(V) uh.assign(Expression(("-Uh*x[1]", "Uh*x[0]"), Uh=Uh, degree=3)) psi0_expression = GaussianPulse(center=(xc, yc), sigma=float(sigma), U=[Uh, Uh], time=0.0, height=1.0, degree=3) # Generate particles
def create_slice(basemesh, point, normal, closest_region=False, crinkle_clip=False): """Create a slicemesh from a basemesh. :param basemesh: Mesh to slice :param point: Point in slicing plane :param normal: Normal to slicing plane :param closest_region: Set to True to extract disjoint region closest to specified point :param crinkle_clip: Set to True to return mesh of same topological dimension as basemesh .. note:: Only 3D-meshes currently supported for slicing. .. warning:: Slice-instances are intended for visualization only, and may produce erronous results if used for computations. """ assert basemesh.geometry().dim() == 3, "Can only slice 3D-meshes." P = np.array([point[0], point[1], point[2]], dtype=np.double) # Create unit normal n = np.array([normal[0],normal[1], normal[2]]) n = n/np.linalg.norm(n) #self.n = Constant((n[0], n[1], n[2])) # Calculate the distribution of vertices around the plane # (sign of np.dot(p-P, n) determines which side of the plane p is on) vsplit = np.dot(basemesh.coordinates()-P, n) # Count each cells number of vertices on the "positive" side of the plane # Only cells with vertices on both sides of the plane intersect the plane operator = np.less npos = np.sum(vsplit[basemesh.cells()] < 0, 1) intersection_cells = basemesh.cells()[(npos > 0) & (npos < 4)] if len(intersection_cells) == 0: # Try to put "zeros" on other side of plane # FIXME: handle cells with vertices exactly intersecting the plane in a more robust manner. operator = np.greater npos = np.sum(vsplit[basemesh.cells()] > 0, 1) #cell_indices = (npos > 0) & (npos < 4) intersection_cells = basemesh.cells()[(npos > 0) & (npos < 4)] if crinkle_clip: cf = CellFunction("size_t", basemesh) cf.set_all(0) cf.array()[(npos>0) & (npos<4)] = 1 mesh = create_submesh(basemesh, cf, 1) else: def add_cell(cells, cell): # Split cell into triangles for i in xrange(len(cell)-2): cells.append(cell[i:i+3]) cells = [] index = 0 indexes = {} for c in intersection_cells: a = operator(vsplit[c], 0) positives = c[np.where(a==True)[0]] negatives = c[np.where(a==False)[0]] cell = [] for pp_ind in positives: pp = basemesh.coordinates()[pp_ind] for pn_ind in negatives: pn = basemesh.coordinates()[pn_ind] if (pp_ind, pn_ind) not in indexes: # Calculate intersection point with the plane d = np.dot(P-pp, n)/np.dot(pp-pn, n) ip = pp+(pp-pn)*d indexes[(pp_ind, pn_ind)] = (index, ip) index += 1 cell.append(indexes[(pp_ind, pn_ind)][0]) add_cell(cells, cell) MPI.barrier(mpi_comm_world()) # Assign global indices # TODO: Assign global indices properly dist = distribution(index) global_idx = sum(dist[:MPI.rank(mpi_comm_world())]) vertices = {} for idx, p in indexes.values(): vertices[idx] = (global_idx, p) global_idx += 1 global_num_cells = MPI.sum(mpi_comm_world(), len(cells)) global_num_vertices = MPI.sum(mpi_comm_world(), len(vertices)) mesh = Mesh() # Return empty mesh if no intersections were found if global_num_cells == 0: mesh_editor = MeshEditor() mesh_editor.open(mesh, "triangle", 2, 3) mesh_editor.init_vertices(0) mesh_editor.init_cells(0) mesh_editor.close() else: # Distribute mesh if empty on any processors cells, vertices = distribute_meshdata(cells, vertices) # Build mesh mesh_editor = MeshEditor() mesh_editor.open(mesh, "triangle", 2, 3) mesh_editor.init_vertices(len(vertices)) mesh_editor.init_cells(len(cells)) for index, cell in enumerate(cells): mesh_editor.add_cell(index, cell[0], cell[1], cell[2]) for local_index, (global_index, coordinates) in vertices.items(): mesh_editor.add_vertex_global(int(local_index), int(global_index), coordinates) mesh_editor.close() mesh.topology().init(0, len(vertices), global_num_vertices) mesh.topology().init(2, len(cells), global_num_cells) if closest_region and mesh.size_global(0) > 0: assert MPI.size(mpi_comm_world())==1, "Extract closest region does not work in parallel" regions = compute_connectivity(mesh) i,d = mesh.bounding_box_tree().compute_closest_entity(Point(P)) if d == MPI.min(mesh.mpi_comm(), d): v = regions[int(i)] else: v = 0 v = MPI.max(mesh.mpi_comm(), v) mesh = create_submesh(mesh, regions, v) return mesh
class FBVP: def __init__(self, mesh_name, parameters, alpha_range=None, beta_range=None): # DEFINE MESH AND PARAMETERS self.parameters = parameters self.mesh_name = mesh_name if not alpha_range: self.alpha_range = parameters.alpha_range else: self.alpha_range = alpha_range if not beta_range: self.beta_range = parameters.beta_range else: self.beta_range = alpha_range meshname = \ f'meshes/{parameters.domain}/{parameters.domain}_{self.mesh_name}.xdmf' self.mesh = Mesh() with XDMFFile(meshname) as f: f.read(self.mesh) print(f'Mesh size= {self.mesh.hmax()}') # dimension of approximation space self.dim = len(self.mesh.coordinates()) print(f'Dimension of solution space is {self.dim}') self.V = FunctionSpace(self.mesh, 'CG', 1) # CG = P1 self.coords = self.mesh.coordinates()[dof_to_vertex_map(self.V)] # self.t_V = FunctionSpace(self.t_mesh, 'CG', 1) self.T = self.parameters.T # final time self.w = TrialFunction(self.V) self.u = TestFunction(self.V) ####################################################################### # CONTROL SET CREATION self.ctrset_alpha = np.linspace(self.alpha_range[0], self.alpha_range[1], parameters.alpha_size) self.ctrset_beta = np.linspace(self.beta_range[0], self.beta_range[1], parameters.beta_size) self.csize_alpha = len(self.ctrset_alpha) self.csize_beta = len(self.ctrset_beta) print(f'Discretized control set has size' f' {self.csize_alpha * self.csize_beta}') ####################################################################### # BOUNDARY CONDITIONS parameters.set_boundary_conditions(self.mesh) self.boundary_markers = MeshFunction('size_t', self.mesh, 1) self.boundary_markers.set_all(99) # pylint: disable=no-member for i, omega in self.parameters.omegas.items(): omega.mark(self.boundary_markers, i) self.ds = Measure('ds', domain=self.mesh, subdomain_data=self.boundary_markers) self.dirichlet_bcs = [ DirichletBC(self.V, parameters.RHS_bound[i], self.boundary_markers, i) for i in parameters.omegas ] # Get indices of dirichlet and dofs self.boundary_nodes_list = \ DirichletBC(self.V, Constant('0.0'), 'on_boundary').get_boundary_values().keys() ####################################################################### # ASSEMBLY time_start = time.process_time() self.assemble_diagonal_matrix() # auxilliary generic diagonal matrix # used for vector*matrix multiplication of dolfin matrices self.assemble_lumpedmm() # assembly lumped mass matrix # which serves the role of identity operator self.assemble_laplacian() # assembly of discrete laplacian self.ad_data_path = f'out/{parameters.experiment}' Path(self.ad_data_path).mkdir(parents=True, exist_ok=True) # FIXME: value of timestep required to impose monotonity may be # different at different times, at this moment code assumes that # it won't be smaller than in case t=T self.timesteps = parameters.get_number_of_timesteps() self.assemble_HJBe() # assembly of explicit operators self.assemble_HJBi() # assembly of implicit operators self.assemble_RHS() # assembly of forcing term print('Final time assembly complete') print(f'Assembly took {time.process_time() - time_start} seconds') print('===========================================================') def assemble_diagonal_matrix(self): print("Assembling auxilliary diagonal matrix") """ Operator assembled to get the right sparsity pattern (non-zero terms can only exist on diagonal) """ mesh3 = UnitIntervalMesh(self.dim) V3 = FunctionSpace(mesh3, "DG", 0) wid = TrialFunction(V3) uid = TestFunction(V3) self.diag_matrix = assemble(uid * wid * dx) self.diag_matrix.zero() def assemble_lumpedmm(self): """ Assembly lumped mass matrix - plays role of identity matrix """ print("Assembling lumped mass matrix") mass_form = self.w * self.u * dx self.mass_matrix = assemble(mass_form) mass_action_form = action(mass_form, Constant(1)) self.MM_terms = assemble(mass_action_form) self.mass_matrix.zero() self.mass_matrix.set_diagonal(self.MM_terms) self.scipy_mass_matrix = toscipy(self.mass_matrix) def assemble_laplacian(self): print("Assembling laplacians") # laplacian discretisation self.laplacian = assemble(dot(grad(self.w), grad(self.u)) * dx) def assemble_HJBe(self, t=None): """ Assembly explicit operator for every control in the control set, then apply artificial diffusion to all of them. Note that artificial diffusion is calculated differently on the boundary nodes. """ self.explicit_matrices = np.empty([self.csize_beta, self.csize_alpha], dtype=object) self.explicit_diffusion = np.empty([self.csize_beta, self.csize_alpha], dtype=object) diag_vector = Vector(self.mesh.mpi_comm(), self.dim) global_min_timestep = float('inf') # Create explicit operator for each control in control set for ind_a, a in enumerate(self.ctrset_alpha): for ind_b, b in enumerate(self.ctrset_beta): if (ind_a * self.csize_beta + ind_b) % 10 == 0: print(f'Assembling explicit matrix under control ' f'{ind_a*self.csize_beta + ind_b} out of ' f'{self.csize_alpha*self.csize_beta}') # coefficients in the interior advection_x = self.parameters.adv_x(a, b) advection_y = self.parameters.adv_y(a, b) reaction = self.parameters.lin(a, b) if t is not None: advection_x.t = t advection_y.t = t reaction.t = t # Discretize PDE (set values on boundary rows to zero) b = np.array([advection_x, advection_y]) E_int_form = (np.dot(b, grad(self.w)) + reaction * self.w) * self.u * dx explicit_matrix = assemble(E_int_form) # Calculate nodewise minimum diffusion required to # impose monotonicity min_diffusion = [0] * explicit_matrix.size(0) for rows, row_num in getrows(explicit_matrix, self.laplacian, ignore=self.boundary_nodes_list): min_diffusion[row_num] = calc_ad(rows, row_num) self.explicit_diffusion[ind_b][ind_a] = min_diffusion diffusion_matrix = apply_ad(self.laplacian, min_diffusion) explicit_matrix += diffusion_matrix explicit_matrix.get_diagonal(diag_vector) current_min_timestep = get_min_timestep( diag_vector, self.MM_terms, self.boundary_nodes_list) global_min_timestep = min(current_min_timestep, global_min_timestep) self.explicit_matrices[ind_b][ind_a] = toscipy(explicit_matrix) ####################################################################### min_timesteps = int(self.T / global_min_timestep) + 1 if not self.timesteps or self.timesteps < min_timesteps: self.timesteps = min_timesteps try: filename = (f'meshes/{self.parameters.domain}/' f'Mvalues-{self.parameters.experiment}.json') with open(filename, 'r') as f: min_timesteps_dict = json.load(f) except FileNotFoundError: min_timesteps_dict = {} min_timesteps_dict[self.parameters.mesh_name] = self.timesteps with open(filename, 'w') as f: json.dump(min_timesteps_dict, f) self.timestep_size = self.T / self.timesteps # time step size self.parameters.calculate_save_interval() self.explicit_matrices = [[ self.timestep_size * E - self.scipy_mass_matrix for E in Elist ] for Elist in self.explicit_matrices] print('Checking if the explicit operators satisfy' ' monotonicity conditions') for matrix_list in self.explicit_matrices: for matrix in matrix_list: explicit_check(matrix, self.boundary_nodes_list) def assemble_RHS(self, t=None): """Assemble right hand side of the FBVP """ print('Assembling RHS') self.forcing_terms = np.empty( [self.csize_beta, self.csize_alpha, self.dim]) for ind_a, alpha in enumerate(self.ctrset_alpha): for ind_b, beta in enumerate(self.ctrset_beta): rhs = self.parameters.RHSt(alpha, beta) if t is not None: rhs.t = t # Initialise forcing term F = np.array(assemble(rhs * self.u * dx)[:]) self.forcing_terms[ind_b][ind_a] = self.timestep_size * F def assemble_HJBi(self, t=None): """ Assembly matrix discretizing second order terms after diffusion moved to the explicit operator is subtracted. Whenever amount of diffusion used to make some row of an explicit operator monotonic exceeds the amount of natural diffusion at that node we call it artificial diffusion. In such case, this row of the implicit operator is multiplied by zero """ print('Assembling implicit operators') self.implicit_matrices = np.empty([self.csize_beta, self.csize_alpha], dtype=object) remaining_diffusion = Function(self.V) max_art_dif = 0.0 max_art_dif_loc = None diffusion_matrix = self.diag_matrix.copy() for ind_a, alpha in enumerate(self.ctrset_alpha): for ind_b, beta in enumerate(self.ctrset_beta): # TODO: Include assert making sure that diffusion is # non-negative on all of the internal nodes diffusion = self.parameters.diffusion(alpha, beta) if t is not None: diffusion.t = t diff = interpolate(diffusion, self.V).vector() if not np.all(diff >= 0): raise Exception("Choose non-negative diffusion") diff_vec = np.array([ diff[i] if i not in self.boundary_nodes_list else 0.0 for i in range(self.dim) ]) artdif = self.explicit_diffusion[ind_b][ind_a] - diff_vec if np.amax(artdif) > max_art_dif: max_art_dif = np.amax(artdif) max_art_dif_loc = self.coords[np.argmax(artdif)] # discretise second order terms remaining_diffusion.vector()[:] = np.maximum( -artdif, [0] * self.dim) diffusion_matrix.set_diagonal(remaining_diffusion.vector()) Iab = matmult(diffusion_matrix, self.laplacian) self.implicit_matrices[ind_b][ind_a] = self.timestep_size * \ Iab + self.mass_matrix self.scipy_implicit_matrices = [[toscipy(mat) for mat in Ialist] for Ialist in self.implicit_matrices] print('Checking if the implicit operators satisfy' ' monotonicity conditions') for matrix_list in self.scipy_implicit_matrices: for matrix in matrix_list: implicit_check(matrix) with open(self.ad_data_path + '/ad.txt', 'a') as f: time_str = f' at time {t}\n' if t is not None else '\n' f.write(f'For mesh {self.mesh_name} max value of artificial' ' diffusion coefficient was' f' {max_art_dif} at {max_art_dif_loc}' + time_str)
def __init__( self, time: df.Constant, mesh: df.Mesh, conductivity: Dict[int, df.Expression], conductivity_ratio: Dict[int, df.Expression], cell_function: df.MeshFunction, cell_tags: CellTags, interface_function: df.MeshFunction, interface_tags: InterfaceTags, parameters: CoupledMonodomainParameters, neumann_boundary_condition: Dict[int, df.Expression] = None, v_prev: df.Function = None, surface_to_volume_factor: Union[float, df.Constant] = None, membrane_capacitance: Union[float, df.Constant] = None, ) -> None: self._time = time self._mesh = mesh self._conductivity = conductivity self._cell_function = cell_function self._cell_tags = cell_tags self._interface_function = interface_function self._interface_tags = interface_tags self._parameters = parameters if surface_to_volume_factor is None: surface_to_volume_factor = df.constant(1) if membrane_capacitance is None: membrane_capacitance = df.constant(1) self._chi_cm = df.Constant(surface_to_volume_factor) * df.Constant( membrane_capacitance) if neumann_boundary_condition is None: self._neumann_boundary_condition: Dict[int, df.Expression] = dict() else: self._neumann_boundary_condition = neumann_boundary_condition if not set(conductivity.keys()) == set(conductivity_ratio.keys()): raise ValueError( "intracellular conductivity and lambda does not have natching keys." ) self._lambda = conductivity_ratio # Function spaces self._function_space = df.FunctionSpace(mesh, "CG", 1) # Test and trial and previous functions self._v_trial = df.TrialFunction(self._function_space) self._v_test = df.TestFunction(self._function_space) self._v = df.Function(self._function_space) if v_prev is None: self._v_prev = df.Function(self._function_space) else: # v_prev is shipped from an odesolver. self._v_prev = v_prev _cell_tags = set(self._cell_tags) _cell_function_values = set(self._cell_function.array()) if not _cell_tags <= _cell_function_values.union({None}): msg = f"Cell function does not contain {_cell_tags - _cell_function_values}" raise ValueError(msg) _interface_tags = set(self._interface_tags) _interface_function_values = { *set(self._interface_function.array()), None } if not _interface_tags <= _interface_function_values.union({None}): msg = f"interface function does not contain {_interface_tags - _interface_function_values}." raise ValueError(msg) # Crete integration measures -- Interfaces self._dGamma = df.Measure("ds", domain=self._mesh, subdomain_data=self._interface_function) # Crete integration measures -- Cells self._dOmega = df.Measure("dx", domain=self._mesh, subdomain_data=self._cell_function) # Create variational forms self._timestep = df.Constant(self._parameters.timestep) self._lhs, self._rhs = self._variational_forms() # Preassemble left-hand side (will be updated if time-step changes) self._lhs_matrix = df.assemble(self._lhs) self._rhs_vector = df.Vector(mesh.mpi_comm(), self._lhs_matrix.size(0)) self._lhs_matrix.init_vector(self._rhs_vector, 0) self._linear_solver = create_linear_solver(self._lhs_matrix, self._parameters)
# Timestepping Tend = 2. dt = Constant(0.02) num_steps = np.rint(Tend / float(dt)) # Output directory store_step = 1 outdir = './../../results/SlottedDisk_Rotation_AddDelete/' # Mesh mesh = Mesh('./../../meshes/circle_0.xml') mesh = refine(mesh) mesh = refine(mesh) mesh = refine(mesh) outfile = XDMFFile(mesh.mpi_comm(), outdir + "psi_h.xdmf") # Set slotted disk psi0_expr = SlottedDisk(radius=rdisk, center=[xc, yc], width=rwidth, depth=0., degree=3, lb=lb, ub=ub) # Function space and velocity field W = FunctionSpace(mesh, 'DG', k) psi_h = Function(W) V = VectorFunctionSpace(mesh, 'DG', 3)
def scalar_laplacians( mesh: df.Mesh, markers: Optional[Dict[str, int]] = None, ffun: Optional[MeshFunction] = None, use_krylov_solver: bool = False, krylov_solver_atol: Optional[float] = None, krylov_solver_rtol: Optional[float] = None, krylov_solver_max_its: Optional[int] = None, verbose: bool = False, strict: bool = False, ) -> Dict[str, df.Function]: """ Calculate the laplacians Arguments --------- mesh : dolfin.Mesh A dolfin mesh markers : dict (optional) A dictionary with the markers for the different bondaries defined in the facet function or within the mesh itself. The follwing markers must be provided: 'base', 'lv', 'epi, 'rv' (optional). If the markers are not provided the following default vales will be used: base = 10, rv = 20, lv = 30, epi = 40. fiber_space : str A string on the form {familiy}_{degree} which determines for what space the fibers should be calculated for. use_krylov_solver: bool If True use Krylov solver, by default False krylov_solver_atol: float (optional) If a Krylov solver is used, this option specifies a convergence criterion in terms of the absolute residual. Default: 1e-15. krylov_solver_rtol: float (optional) If a Krylov solver is used, this option specifies a convergence criterion in terms of the relative residual. Default: 1e-10. krylov_solver_max_its: int (optional) If a Krylov solver is used, this option specifies the maximum number of iterations to perform. Default: 10000. verbose: bool If true, print more info, by default False strict: bool If true raise RuntimeError if solutions does not sum to 1.0 """ if not isinstance(mesh, df.Mesh): raise TypeError("Expected a dolfin.Mesh as the mesh argument.") # Init connectivities mesh.init(2) if ffun is None: ffun = df.MeshFunction("size_t", mesh, 2, mesh.domains()) # Boundary markers, solutions and cases cases, boundaries, markers = find_cases_and_boundaries(ffun, markers) markers_str = "\n".join( ["{}: {}".format(k, v) for k, v in markers.items()]) df.info(("Compute scalar laplacian solutions with the markers: \n" "{}").format(markers_str, ), ) check_boundaries_are_marked( mesh=mesh, ffun=ffun, markers=markers, boundaries=boundaries, ) # Compte the apex to base solutons num_vertices = mesh.num_vertices() num_cells = mesh.num_cells() if mesh.mpi_comm().size > 1: num_vertices = mesh.mpi_comm().allreduce(num_vertices) num_cells = mesh.mpi_comm().allreduce(num_cells) df.info(" Num vertices: {0}".format(num_vertices)) df.info(" Num cells: {0}".format(num_cells)) if "mv" in cases and "av" in cases: # Use Doste approach pass # Else use the Bayer approach return bayer( cases=cases, mesh=mesh, markers=markers, ffun=ffun, verbose=verbose, use_krylov_solver=use_krylov_solver, strict=strict, krylov_solver_atol=krylov_solver_atol, krylov_solver_rtol=krylov_solver_rtol, krylov_solver_max_its=krylov_solver_max_its, )