phiFEM 0.7.2.dev2__py3-none-any.whl → 0.8.1.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
phifem/mesh_scripts.py CHANGED
@@ -397,10 +397,50 @@ def _transfer_tags(
397
397
  return dest_entities_tags
398
398
 
399
399
 
400
+ def _remove_isolated_cells(
401
+ mesh: dfx.mesh.Mesh,
402
+ exterior_indices: np.ndarray[np.int32],
403
+ cut_indices: np.ndarray[np.int32],
404
+ interior_indices: np.ndarray[np.int32],
405
+ ):
406
+ cdim = mesh.topology.dim
407
+ vdim = 0
408
+ # Create the cell to facet connectivity and reshape it into an array s.t. c2f_map[cell_index] = [facets of this cell index]
409
+ mesh.topology.create_connectivity(cdim, vdim)
410
+ c2v_connect = mesh.topology.connectivity(cdim, vdim)
411
+ num_vertices_per_cell = len(c2v_connect.links(0))
412
+ c2v_map = np.reshape(c2v_connect.array, (-1, num_vertices_per_cell))
413
+
414
+ mesh.topology.create_connectivity(vdim, cdim)
415
+ v2c_connect = mesh.topology.connectivity(vdim, cdim)
416
+ v2c_map, max_offset = _reshape_map(v2c_connect)
417
+
418
+ neighbor_cells = np.reshape(
419
+ v2c_map[c2v_map[cut_indices]], (-1, num_vertices_per_cell * max_offset)
420
+ )
421
+ mask_no_interior_connected = ~np.any(
422
+ np.isin(neighbor_cells, interior_indices), axis=1
423
+ )
424
+ mask_no_exterior_connected = ~np.any(
425
+ np.isin(neighbor_cells, exterior_indices), axis=1
426
+ )
427
+
428
+ cut_disconnected_interior = cut_indices[mask_no_interior_connected]
429
+ cut_disconnected_exterior = cut_indices[mask_no_exterior_connected]
430
+
431
+ cut_indices = np.setdiff1d(
432
+ cut_indices, np.union1d(cut_disconnected_interior, cut_disconnected_exterior)
433
+ )
434
+ exterior_indices = np.union1d(exterior_indices, cut_disconnected_interior)
435
+ interior_indices = np.union1d(interior_indices, cut_disconnected_exterior)
436
+
437
+ return exterior_indices, cut_indices, interior_indices
438
+
439
+
400
440
  def _tag_cells(
401
441
  mesh: Mesh,
402
442
  levelset_expression: Expression,
403
- single_layer_cut: bool = False,
443
+ single_layer_cut: bool,
404
444
  ) -> MeshTags:
405
445
  """Tag the mesh cells by computing detection = Σ f(dof)/Σ|f(dof)| where 'dof' are coming from a custom quadrature rule with points on the boundary of the cell only.
406
446
  Strictly inside cell => tag 1
@@ -431,26 +471,9 @@ def _tag_cells(
431
471
  cut_indices = np.where(cut)[0]
432
472
 
433
473
  if single_layer_cut:
434
- vdim = 0
435
- # Create the cell to facet connectivity and reshape it into an array s.t. c2f_map[cell_index] = [facets of this cell index]
436
- mesh.topology.create_connectivity(cdim, vdim)
437
- c2v_connect = mesh.topology.connectivity(cdim, vdim)
438
- num_vertices_per_cell = len(c2v_connect.links(0))
439
- c2v_map = np.reshape(c2v_connect.array, (-1, num_vertices_per_cell))
440
-
441
- mesh.topology.create_connectivity(vdim, cdim)
442
- v2c_connect = mesh.topology.connectivity(vdim, cdim)
443
- v2c_map, max_offset = _reshape_map(v2c_connect)
444
-
445
- neighbor_cells = np.reshape(
446
- v2c_map[c2v_map[cut_indices]], (-1, num_vertices_per_cell * max_offset)
447
- )
448
- mask_connected_cut_cells = np.any(
449
- np.isin(neighbor_cells, interior_indices), axis=1
474
+ exterior_indices, cut_indices, interior_indices = _remove_isolated_cells(
475
+ mesh, exterior_indices, cut_indices, interior_indices
450
476
  )
451
- isolated_cut_cells = cut_indices[~mask_connected_cut_cells]
452
- cut_indices = np.setdiff1d(cut_indices, isolated_cut_cells)
453
- exterior_indices = np.union1d(exterior_indices, isolated_cut_cells)
454
477
 
455
478
  if debug_mode:
456
479
  if len(interior_indices) == 0:
@@ -480,7 +503,6 @@ def _tag_cells(
480
503
  cells_tags = dfx.mesh.meshtags(
481
504
  mesh, mesh.topology.dim, indices[sorted_indices], markers[sorted_indices]
482
505
  )
483
-
484
506
  return cells_tags
485
507
 
486
508
 
@@ -506,9 +528,9 @@ def _cells_facets_pairs(f2c_map, c2f_map, facets):
506
528
  def _tag_facets(
507
529
  mesh: Mesh,
508
530
  levelset_expression_facets: Expression,
531
+ single_layer_cut: bool,
509
532
  cells_tags: MeshTags | None = None,
510
533
  levelset_expression_cells: Expression | None = None,
511
- single_layer_cut: bool = False,
512
534
  ) -> MeshTags:
513
535
  """Tag the mesh facets.
514
536
  Strictly interior facets => tag 1
@@ -531,9 +553,7 @@ def _tag_facets(
531
553
  assert levelset_expression_cells is not None, (
532
554
  "You must either pass cells_tags or a levelset expression over the cells of the domain."
533
555
  )
534
- cells_tags = _tag_cells(
535
- mesh, levelset_expression_cells, single_layer_cut=single_layer_cut
536
- )
556
+ cells_tags = _tag_cells(mesh, levelset_expression_cells, single_layer_cut)
537
557
 
538
558
  cdim = mesh.topology.dim
539
559
  fdim = cdim - 1
@@ -579,7 +599,10 @@ def _tag_facets(
579
599
  cut_indices = np.setdiff1d(cut_indices, direct_indices)
580
600
 
581
601
  # Compute the list of facets on the boundary of the union of cut cells
582
- boundary_cut_indices = _compute_subdomain_exterior_facets(mesh, cells_tags, [2])
602
+ if len(cells_tags.find(2)) > 0:
603
+ boundary_cut_indices = _compute_subdomain_exterior_facets(mesh, cells_tags, [2])
604
+ else:
605
+ boundary_cut_indices = []
583
606
  boundary_exterior_indices = np.intersect1d(boundary_cut_indices, interior_indices)
584
607
  boundary_exterior_indices = np.setdiff1d(boundary_exterior_indices, direct_indices)
585
608
  interior_indices = np.setdiff1d(interior_indices, boundary_exterior_indices)
@@ -662,6 +685,60 @@ def _overwrite_tags(mesh, tags_to_overwrite, new_tags):
662
685
  return overwritten_tags
663
686
 
664
687
 
688
+ def _compute_codim_interpolation_points(
689
+ space: dfx.fem.FunctionSpace,
690
+ ) -> np.ndarray[np.float64]:
691
+ """Compute the same FunctionSpace but defined over a submesh of codim 1.
692
+
693
+ Args:
694
+ space: the original space.
695
+
696
+ Return: a new space defined on a mesh of codim 1.
697
+ """
698
+ family_name = space.ufl_element().basix_element.family.name
699
+ degree = space.ufl_element().basix_element.degree
700
+ tdim = space.mesh.topology.dim
701
+ codim_mesh = dfx.mesh.create_submesh(space.mesh, tdim - 1, np.array([0.0]))[0]
702
+ codim_cell_name = codim_mesh.topology.cell_name()
703
+ new_elmt = element(family_name, codim_cell_name, degree)
704
+ new_space = dfx.fem.functionspace(codim_mesh, new_elmt)
705
+ return new_space.element.interpolation_points()
706
+
707
+
708
+ def _levelset_expression(
709
+ mesh: dfx.mesh.Mesh,
710
+ levelset: Function | Callable,
711
+ interpolation_points: np.NDarray[np.float64],
712
+ ) -> Expression:
713
+ """Sanitize the levelset input by turning it into an Expression.
714
+
715
+ Args:
716
+ levelset: the levelset function.
717
+ detection_space: the detection space.
718
+
719
+ Return: the levelset as an Expression object.
720
+ """
721
+
722
+ # Sanitize levelset input
723
+ try:
724
+ # Test if levelset is a dolfinx.fem.Function
725
+ _ = levelset.function_space
726
+ levelset_expression = dfx.fem.Expression(
727
+ levelset, interpolation_points, comm=mesh.comm
728
+ )
729
+ except AttributeError:
730
+ try:
731
+ x = ufl.SpatialCoordinate(mesh)
732
+ levelset_expression = dfx.fem.Expression(
733
+ levelset(x), interpolation_points, comm=mesh.comm
734
+ )
735
+ except TypeError:
736
+ print(
737
+ "Invalid levelset type: must be either a dolfinx.fem.Function or a UFL based Callable."
738
+ )
739
+ return levelset_expression
740
+
741
+
665
742
  def compute_tags_measures(
666
743
  mesh: Mesh,
667
744
  levelset: Function | Callable,
@@ -692,27 +769,23 @@ def compute_tags_measures(
692
769
  The boundaries measure.
693
770
  Submesh c-map, v-map and n-map.
694
771
  """
772
+ interpolation_points = detection_space.element.interpolation_points()
773
+ levelset_expression = _levelset_expression(mesh, levelset, interpolation_points)
695
774
 
696
- # Sanitize levelset input
697
- try:
698
- levelset_expression = dfx.fem.Expression(
699
- levelset, detection_space.element.interpolation_points()
700
- )
701
- except AttributeError:
702
- try:
703
- x = mesh.SpatialCoordinate(mesh)
704
- levelset_expression = dfx.fem.Expression(
705
- levelset(x), detection_space.element.interpolation_points()
706
- )
707
- except TypeError:
708
- print(
709
- "Invalid levelset type: must be either a dolfinx.fem.Function or a UFL based Callable."
710
- )
775
+ cells_tags = _tag_cells(mesh, levelset_expression, single_layer_cut)
711
776
 
712
- cells_tags = _tag_cells(
713
- mesh, levelset_expression, single_layer_cut=single_layer_cut
777
+ codim_interpolation_points = _compute_codim_interpolation_points(detection_space)
778
+ levelset_expression_facets = _levelset_expression(
779
+ mesh, levelset, codim_interpolation_points
780
+ )
781
+
782
+ facets_tags = _tag_facets(
783
+ mesh,
784
+ levelset_expression_facets,
785
+ single_layer_cut,
786
+ cells_tags=cells_tags,
787
+ levelset_expression_cells=levelset_expression,
714
788
  )
715
- facets_tags = _tag_facets(mesh, cells_tags, discrete_levelset, detection_degree)
716
789
 
717
790
  if overwrite_tags is not None:
718
791
  if "cells" in overwrite_tags:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: phiFEM
3
- Version: 0.7.2.dev2
3
+ Version: 0.8.1.dev1
4
4
  Summary: phiFEM: convenience tools to use the phiFEM immersed boundary finite element method with dolfinx.
5
5
  Project-URL: Homepage, https://github.com/PhiFEM/phiFEM
6
6
  Project-URL: Issues, https://github.com/PhiFEM/phiFEM/issues
@@ -0,0 +1,7 @@
1
+ phifem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ phifem/mesh_scripts.py,sha256=AAUsA75d8UVjKTcjbie9Ap9GS0zFBR2rvNPRiAfT9L0,33222
3
+ phifem-0.8.1.dev1.dist-info/METADATA,sha256=9N9PgoSH6zNVh_jN3XSkp_QVseI74H8y-3yYRESOLRM,5606
4
+ phifem-0.8.1.dev1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
5
+ phifem-0.8.1.dev1.dist-info/licenses/COPYING,sha256=xTplwv1WHIfqq_EHLvXcq4ZTBCvBUwhGX1JBNYXrYnE,35146
6
+ phifem-0.8.1.dev1.dist-info/licenses/COPYING.lesser,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
7
+ phifem-0.8.1.dev1.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- phifem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- phifem/mesh_scripts.py,sha256=Im8vPg3NxA44qiynUmVkCvyu6C6tYcQLKhJpDY0FJbA,30820
3
- phifem-0.7.2.dev2.dist-info/METADATA,sha256=pggXcQxyYtwUxScyjZtHvFlmkiakNl8kLtbr9qCdj4s,5606
4
- phifem-0.7.2.dev2.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
5
- phifem-0.7.2.dev2.dist-info/licenses/COPYING,sha256=xTplwv1WHIfqq_EHLvXcq4ZTBCvBUwhGX1JBNYXrYnE,35146
6
- phifem-0.7.2.dev2.dist-info/licenses/COPYING.lesser,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
7
- phifem-0.7.2.dev2.dist-info/RECORD,,