diff --git a/docs/.doctrees/documentation.doctree b/docs/.doctrees/documentation.doctree index 6c4a506..97360ea 100644 Binary files a/docs/.doctrees/documentation.doctree and b/docs/.doctrees/documentation.doctree differ diff --git a/docs/.doctrees/environment.pickle b/docs/.doctrees/environment.pickle index b9626c6..d16a5e8 100644 Binary files a/docs/.doctrees/environment.pickle and b/docs/.doctrees/environment.pickle differ diff --git a/docs/.doctrees/index.doctree b/docs/.doctrees/index.doctree index 31fca9f..72e4b87 100644 Binary files a/docs/.doctrees/index.doctree and b/docs/.doctrees/index.doctree differ diff --git a/docs/.doctrees/readme_link.doctree b/docs/.doctrees/readme_link.doctree index 085e548..ca214bf 100644 Binary files a/docs/.doctrees/readme_link.doctree and b/docs/.doctrees/readme_link.doctree differ diff --git a/docs/.doctrees/tutorials.doctree b/docs/.doctrees/tutorials.doctree index a2fe62c..8b6cdec 100644 Binary files a/docs/.doctrees/tutorials.doctree and b/docs/.doctrees/tutorials.doctree differ diff --git a/docs/.doctrees/waveguicsx.doctree b/docs/.doctrees/waveguicsx.doctree index aa31872..b769822 100644 Binary files a/docs/.doctrees/waveguicsx.doctree and b/docs/.doctrees/waveguicsx.doctree differ diff --git a/docs/_modules/index.html b/docs/_modules/index.html index 5dc1e3e..c90ae92 100644 --- a/docs/_modules/index.html +++ b/docs/_modules/index.html @@ -57,14 +57,14 @@
  • Tutorials
  • diff --git a/docs/_sources/tutorials.rst.txt b/docs/_sources/tutorials.rst.txt index 8fbffff..c2a55b7 100644 --- a/docs/_sources/tutorials.rst.txt +++ b/docs/_sources/tutorials.rst.txt @@ -2,8 +2,8 @@ Tutorials ========= -3D elastic bar of square cross-section --------------------------------------- +0. Three-dimensional elastic bar of square cross-section +-------------------------------------------------------- 3D (visco-)elastic waveguide example. The cross-section is a 2D square with free boundary conditions on its boundaries. @@ -15,8 +15,8 @@ Results are compared with Euler-Bernoulli analytical solutions for the lowest wa .. literalinclude:: ../examples/Elastic_Waveguide_SquareBar3D.py -3D elastic bar of square cross-section with parallelization ------------------------------------------------------------ +1. Three-dimensional elastic bar of square cross-section with parallelization +----------------------------------------------------------------------------- 3D (visco-)elastic waveguide example. The cross-section is a 2D square with free boundary conditions on its 1D boundaries. @@ -30,8 +30,8 @@ In this example: .. literalinclude:: ../examples/Elastic_Waveguide_SquareBar3D_ParallelizedLoop.py -3D elastic bar of square cross-section buried into a PML external medium ------------------------------------------------------------------------- +2. Three-dimensional elastic bar of square cross-section buried into a PML external medium +------------------------------------------------------------------------------------------ 3D (visco-)elastic waveguide example. The cross-section is a 2D square buried into a PML external elastic medium. @@ -43,8 +43,8 @@ Results are to be compared with Fig. 8 of paper: Treyssede, Journal of Computati .. literalinclude:: ../examples/Elastic_Waveguide_SquareBar3D_PML.py -3D elastic bar of square cross-section buried into a PML external medium using gmsh -------------------------------------------------------------------------------------- +3. Three-dimensional elastic bar of square cross-section buried into a PML external medium using gmsh +----------------------------------------------------------------------------------------------------- 3D (visco-)elastic waveguide example. The cross-section is a 2D square buried into a PML external elastic medium. @@ -57,8 +57,8 @@ Results are to be compared with Fig. 8 of paper: Treyssede, Journal of Computati .. literalinclude:: ../examples/Elastic_Waveguide_SquareBar3D_PML_Gmsh.py -Excitation of a 3D elastic bar of circular cross-section --------------------------------------------------------- +4. Excitation of a three-dimensional elastic bar of circular cross-section +-------------------------------------------------------------------------- 3D elastic waveguide example. The cross-section is a 2D circle with free boundary conditions on its 1D boundaries, material: elastic steel. @@ -69,8 +69,8 @@ Results are to be compared with Figs. 5, 6 and 7 of paper: Treyssede, Wave Motio .. literalinclude:: ../examples/Elastic_Waveguide_CircularBar3D_ForcedResponse_Gmsh.py -Excitation of a 3D elastic bar of circular cross-section with parallelization ------------------------------------------------------------------------------ +5. Excitation of a three-dimensional elastic bar of circular cross-section with parallelization +----------------------------------------------------------------------------------------------- 3D elastic waveguide example. The cross-section is a 2D circle with free boundary conditions on its 1D boundaries, material: elastic steel. @@ -85,8 +85,8 @@ In this example: .. literalinclude:: ../examples/Elastic_Waveguide_CircularBar3D_ForcedResponse_Gmsh_ParallelizedLoop.py -Time response of a plate excited near its first ZGV resonance -------------------------------------------------------------- +6. Time response of a two-dimensional plate excited near its first ZGV resonance +-------------------------------------------------------------------------------- 2D (visco-)elastic waveguide example (Lamb modes in a plate excited near 1st ZGV resonance). The cross-section is a 1D line with free boundary conditions on its boundaries. @@ -99,8 +99,8 @@ Note: the depth direction is x, the axis of propagation is z. .. literalinclude:: ../examples/Elastic_Waveguide_Plate2D_TransientResponse.py -Dispersion curves of a rail ---------------------------- +7. Dispersion curves of a rail +------------------------------ 3D elastic waveguide example. The cross-section is a 2D rail profile (60E1, 60.21kg/m) with free boundary conditions on its 1D boundaries, material: elastic steel. diff --git a/docs/documentation.html b/docs/documentation.html new file mode 100644 index 0000000..060eceb --- /dev/null +++ b/docs/documentation.html @@ -0,0 +1,174 @@ + + + + + + + Documentation — waveguicsx 1.1 documentation + + + + + + + + + + + + + + + + + + + +
    + + +
    + + +
    +
    + + + + \ No newline at end of file diff --git a/docs/genindex.html b/docs/genindex.html index 774c1e3..1651520 100644 --- a/docs/genindex.html +++ b/docs/genindex.html @@ -57,14 +57,14 @@
  • Tutorials
  • diff --git a/docs/index.html b/docs/index.html index 2a3294c..3cacdf2 100644 --- a/docs/index.html +++ b/docs/index.html @@ -59,14 +59,14 @@
  • Tutorials
  • @@ -162,14 +162,14 @@

    Waveguicsx - Version 1.1Tutorials diff --git a/docs/objects.inv b/docs/objects.inv index aa49b35..dcbcfd3 100644 Binary files a/docs/objects.inv and b/docs/objects.inv differ diff --git a/docs/py-modindex.html b/docs/py-modindex.html index ace55e7..53cfb39 100644 --- a/docs/py-modindex.html +++ b/docs/py-modindex.html @@ -60,14 +60,14 @@
  • Tutorials
  • diff --git a/docs/readme_link.html b/docs/readme_link.html index d75acd6..6ce9168 100644 --- a/docs/readme_link.html +++ b/docs/readme_link.html @@ -62,14 +62,14 @@
  • Tutorials
  • diff --git a/docs/search.html b/docs/search.html index eee319f..11d10d5 100644 --- a/docs/search.html +++ b/docs/search.html @@ -60,14 +60,14 @@
  • Tutorials
  • diff --git a/docs/searchindex.js b/docs/searchindex.js index 46381de..760f51d 100644 --- a/docs/searchindex.js +++ b/docs/searchindex.js @@ -1 +1 @@ -Search.setIndex({"docnames": ["documentation", "index", "readme_link", "tutorials", "waveguicsx"], "filenames": ["documentation.rst", "index.rst", "readme_link.md", "tutorials.rst", "waveguicsx.rst"], "titles": ["Documentation", "Waveguicsx - Version 1.1", "Presentation", "Tutorials", "waveguicsx.waveguide"], "terms": {"waveguicsx": [0, 3], "waveguid": [0, 1, 2, 3], "set_paramet": [0, 1, 2, 3, 4], "solv": [0, 1, 2, 3, 4], "compute_eigenforc": [0, 1, 4], "compute_poynting_norm": [0, 1, 4], "compute_energy_veloc": [0, 1, 2, 3, 4], "compute_opposite_go": [0, 1, 4], "compute_group_veloc": [0, 1, 2, 4], "compute_traveling_direct": [0, 1, 3, 4], "compute_pml_ratio": [0, 1, 4], "compute_response_coeffici": [0, 1, 2, 3, 4], "compute_respons": [0, 1, 2, 3, 4], "plot_phase_veloc": [0, 1, 3, 4], "plot_attenu": [0, 1, 3, 4], "plot_energy_veloc": [0, 1, 2, 3, 4], "plot_group_veloc": [0, 1, 3, 4], "plot_coeffici": [0, 1, 3, 4], "plot_excit": [0, 1, 3, 4], "plot": [0, 1, 2, 3, 4], "set_plot_scal": [0, 1, 2, 3, 4], "plot_spectrum": [0, 1, 2, 3, 4], "signal": [0, 1, 2, 3, 4], "fft": [0, 1, 2, 4], "ifft": [0, 1, 2, 3, 4], "ricker": [0, 1, 4], "toneburst": [0, 1, 2, 3, 4], "chirp": [0, 1, 4], "1": [0, 3, 4], "present": 1, "0": [1, 3, 4], "introduct": 1, "basic": [1, 3], "exampl": [1, 3, 4], "2": [1, 3, 4], "prerequisit": 1, "3": [1, 3, 4], "instal": 1, "clone": 1, "public": [1, 3], "repositori": 1, "gener": [1, 3, 4], "docker": 1, "imag": [1, 3, 4], "run": 1, "contain": [1, 3, 4], "us": [1, 4], "4": [1, 3, 4], "document": [1, 3, 4], "5": [1, 3, 4], "tutori": [1, 4], "6": [1, 3], "author": 1, "contributor": 1, "7": [1, 3], "how": 1, "cite": 1, "8": [1, 3, 4], "licens": [1, 3], "3d": [1, 4], "elast": [1, 2], "bar": [1, 2], "squar": 1, "cross": [1, 2, 4], "section": [1, 2, 4], "parallel": [1, 2, 4], "buri": [1, 2, 4], "pml": [1, 2, 4], "extern": 1, "medium": 1, "gmsh": 1, "excit": [1, 2, 4], "circular": 1, "time": [1, 2, 4], "respons": [1, 2, 4], "plate": [1, 2, 4], "its": 1, "first": [1, 2, 4], "zgv": 1, "reson": 1, "dispers": [1, 2, 4], "curv": [1, 2, 4], "rail": [1, 2], "python": [2, 3, 4], "librari": [2, 3], "complex": [2, 3, 4], "problem": [2, 3, 4], "copyright": [2, 3], "c": [2, 3, 4], "2023": [2, 3], "2024": [2, 3], "fabien": [2, 3], "treyssed": [2, 3], "thi": [2, 3, 4], "file": [2, 3], "i": [2, 3, 4], "part": [2, 3, 4], "free": [2, 3, 4], "softwar": [2, 3], "you": 2, "can": [2, 3, 4], "redistribut": 2, "modifi": 2, "under": [2, 3], "term": [2, 4], "gnu": [2, 3], "publish": 2, "foundat": 2, "either": [2, 4], "version": 2, "your": 2, "option": [2, 4], "ani": [2, 4], "later": 2, "distribut": [2, 3], "hope": 2, "without": 2, "warranti": 2, "even": [2, 4], "impli": 2, "merchant": 2, "fit": 2, "FOR": 2, "A": [2, 4], "particular": 2, "purpos": 2, "see": [2, 3, 4], "more": [2, 3], "detail": 2, "should": [2, 3, 4], "have": 2, "receiv": [2, 4], "copi": 2, "along": [2, 3, 4], "If": [2, 4], "http": [2, 3], "www": 2, "org": [2, 3], "contact": 2, "univ": 2, "eiffel": 2, "fr": 2, "base": [2, 4], "slepc": [2, 3, 4], "eigensolv": [2, 4], "The": [2, 3, 4], "full": [2, 3, 4], "entir": [2, 3, 4], "defin": [2, 3, 4], "py": [2, 3, 4], "modul": [2, 3, 4], "deal": [2, 4], "two": [2, 3, 4], "dimension": [2, 3, 4], "e": [2, 3, 4], "g": [2, 3, 4], "three": [2, 3, 4], "arbitrarili": [2, 4], "shape": [2, 3, 4], "inhomogen": [2, 4], "transvers": [2, 4], "direct": [2, 3, 4], "anisotrop": [2, 4], "valu": [2, 3, 4], "handl": [2, 3, 4], "includ": [2, 3, 4], "effect": [2, 4], "non": [2, 4], "propag": [2, 3, 4], "mode": [2, 3, 4], "evanesc": [2, 4], "viscoelast": [2, 3, 4], "loss": [2, 3, 4], "materi": [2, 3, 4], "properti": [2, 3, 4], "perfectli": [2, 4], "match": [2, 4], "layer": [2, 4], "simul": [2, 4], "precis": 2, "follow": [2, 3, 4], "matrix": [2, 3, 4], "textbf": [2, 3], "k": [2, 3, 4], "_0": 2, "omega": [2, 3, 4], "m": [2, 3, 4], "text": [2, 3], "_1": [2, 3], "t": [2, 3, 4], "_2": [2, 3], "u": [2, 3, 4], "f": [2, 3, 4], "kind": [2, 4], "typic": [2, 4], "stem": [2, 4], "from": [2, 3, 4], "so": [2, 4], "call": [2, 3, 4], "semi": [2, 4], "analyt": [2, 3, 4], "finit": [2, 3, 4], "element": [2, 3, 4], "fe": [2, 3], "method": [2, 3], "refer": 2, "below": [2, 3], "theoret": [2, 4], "input": [2, 3, 4], "ar": [2, 3, 4], "matric": [2, 3, 4], "petsc": [2, 3, 4], "format": 2, "comput": [2, 3, 4], "well": [2, 4], "vector": [2, 3, 4], "forc": [2, 3, 4], "requir": [2, 4], "These": 2, "built": [2, 3], "own": 2, "favorit": 2, "code": 2, "In": [2, 3, 4], "case": [2, 3, 4], "just": 2, "need": [2, 4], "import": [2, 3, 4], "convert": 2, "them": [2, 3, 4], "do": [2, 3], "open": [2, 3], "fenicsx": [2, 3], "shown": [2, 4], "correspond": [2, 4], "an": [2, 3, 4], "eigenvalu": [2, 3, 4], "iter": [2, 4], "vari": [2, 3, 4], "paramet": [2, 3], "which": [2, 3, 4], "angular": [2, 3, 4], "frequenc": [2, 3, 4], "wavenumb": [2, 3, 4], "lead": [2, 3, 4], "result": [2, 3, 4], "former": [2, 4], "while": [2, 4], "latter": [2, 4], "loop": [2, 3, 4], "over": [2, 4], "some": [2, 3, 4], "mpi4pi": [2, 3, 4], "variou": [2, 4], "modal": [2, 4], "energi": [2, 3, 4], "veloc": [2, 3, 4], "group": [2, 3, 4], "post": [2, 3, 4], "process": [2, 3, 4], "function": [2, 3, 4], "repons": [2, 4], "neq": 2, "domain": [2, 3, 4], "expand": [2, 4], "solut": [2, 3, 4], "sum": [2, 3, 4], "eigenmod": [2, 3, 4], "biorthogon": [2, 4], "relationship": [2, 4], "veri": [2, 4], "fast": [2, 4], "wavefield": [2, 4], "transient": [2, 4], "final": [2, 3], "invers": [2, 4], "class": [2, 4], "main": 2, "enabl": [2, 3, 4], "other": 2, "provid": 2, "easili": 2, "transform": [2, 3, 4], "puls": 2, "homogen": 2, "note": [2, 3, 4], "previous": 2, "store": [2, 3, 4], "binari": 2, "want": 2, "convers": 2, "given": [2, 4], "2d": [2, 3, 4], "numpi": [2, 3, 4], "arrai": [2, 3, 4], "dens": 2, "mat": [2, 4], "createdens": 2, "spars": 2, "matlab": 2, "scipi": 2, "io": [2, 3], "loadmat": 2, "matlab_fil": 2, "here": [2, 3, 4], "suppos": 2, "variabl": [2, 4], "name": [2, 4], "tocsr": 2, "csr": 2, "createaij": 2, "size": [2, 3, 4], "indptr": 2, "indic": 2, "data": [2, 4], "mpi": [2, 3, 4], "petsc4pi": [2, 3, 4], "np": [2, 3, 4], "matplotlib": [2, 3, 4], "pyplot": [2, 3], "plt": [2, 3, 4], "load": 2, "k0": [2, 3, 4], "k1": [2, 3, 4], "k2": [2, 3, 4], "save": [2, 3], "basicexample_k0k1k2mf": [2, 3], "dat": [2, 3], "thick": [2, 3], "poisson": 2, "ratio": [2, 3, 4], "It": 2, "found": [2, 4], "subfold": 2, "elastic_waveguide_plate2d_transientrespons": 2, "viewer": [2, 3], "createbinari": [2, 3], "r": 2, "must": [2, 3, 4], "order": [2, 3, 4], "object": [2, 3, 4], "been": [2, 4], "initi": [2, 3, 4], "wg": [2, 3, 4], "comm_world": [2, 3, 4], "arang": [2, 3], "10": [2, 3], "set": [2, 3, 4], "rang": [2, 3, 4], "normal": [2, 3, 4], "uncom": [2, 3], "line": [2, 3], "instead": [2, 3, 4], "reduc": [2, 3, 4], "also": [2, 3, 4], "nev": [2, 3, 4], "20": [2, 3], "target": [2, 3, 4], "access": [2, 3, 4], "eigensolut": [2, 4], "iomega": [2, 4], "imod": [2, 3, 4], "eigenvector": [2, 3, 4], "idof": [2, 3, 4], "default": [2, 4], "v": [2, 3, 4], "show": [2, 3, 4], "h": [2, 3, 4], "rho": [2, 3], "01": [2, 3, 4], "3260": [2, 3], "7800": [2, 3], "shear": [2, 3], "wave": [2, 3, 4], "celer": [2, 3], "": [2, 3], "densiti": [2, 3], "kg": [2, 3], "length": [2, 3, 4], "mass": [2, 3, 4], "dim": [2, 3, 4], "characterist": [2, 3, 4], "hz": [2, 3, 4], "user": [2, 4], "unit": [2, 3, 4], "mhz": [2, 3], "mm": [2, 3], "plot_scal": [2, 3, 4], "energy_veloc": [2, 3, 4], "1000": [2, 3], "sc": [2, 3, 4], "posit": [2, 4], "go": [2, 4], "ax": [2, 3, 4], "set_xlabel": [2, 3], "set_ylabel": [2, 3], "vec": [2, 3, 4], "3218": [2, 3], "m3": [2, 3], "number": [2, 3, 4], "request": [2, 4], "each": [2, 3, 4], "spectrum": [2, 3, 4], "400e3": 2, "2e": [2, 3], "fc": [2, 3, 4], "100e3": [2, 4], "n": [2, 3, 4], "central": [2, 4], "100": [2, 3, 4], "khz": [2, 3], "cycl": [2, 4], "durat": [2, 4], "pi": [2, 3, 4], "rad": [2, 3], "becaus": [2, 4], "otherwis": [2, 3, 4], "comment": 2, "coeffici": [2, 3, 4], "due": [2, 4], "degre": [2, 4], "freedom": [2, 4], "dof": [2, 3, 4], "point": [2, 3, 4], "appli": [2, 4], "bottom": 2, "surfac": [2, 3], "38": 2, "index": [2, 3, 4], "x": [2, 3, 4], "compon": [2, 3, 4], "top": 2, "ab": [2, 3, 4], "norm": [2, 3], "log": [2, 3, 4], "color": [2, 3, 4], "modulu": 2, "colorbar": 2, "set_label": 2, "set_ylim": [2, 3], "y": [2, 3, 4], "limit": [2, 4], "necessari": [2, 3], "set_clim": 2, "1e": [2, 3], "14": 2, "11": [2, 3], "axial": [2, 4], "coordin": [2, 3, 4], "z": [2, 3, 4], "50": [2, 3, 4], "fals": [2, 3, 4], "slepc4pi": [2, 3], "platform": [2, 3], "To": 2, "therefor": 2, "thei": [2, 3, 4], "conveni": 2, "editor": 2, "jupyt": [2, 3], "notebook": [2, 3], "current": 2, "written": 2, "v0": 2, "minor": 2, "modif": 2, "mai": [2, 4], "latest": 2, "work": [2, 3], "progress": 2, "git": 2, "github": [2, 3], "com": [2, 3], "universit": 2, "gustav": 2, "cd": 2, "depend": 2, "nevertheless": 2, "we": 2, "recommend": 2, "dolfinx": [2, 3], "sourc": [2, 3, 4], "befor": [2, 4], "root": [2, 3], "Then": 2, "shell": 2, "script": 2, "launch_fenicsx": 2, "sh": 2, "insid": [2, 3, 4], "take": 2, "onc": 2, "launch": 2, "packag": 2, "onli": [2, 3, 4], "waveguicsxus": 2, "hostnam": 2, "python3": [2, 3], "pip": 2, "warn": [2, 4], "folder": 2, "mount": 2, "home": 2, "all": [2, 3, 4], "chang": [2, 4], "persist": 2, "host": 2, "system": [2, 3], "local": [2, 3, 4], "ignor": 2, "reinstal": 2, "usag": [2, 3], "real": [2, 3, 4], "back": 2, "elastic_waveguide_squarebar3d": 2, "cli": 2, "exit": 2, "leav": 2, "build": [2, 3], "setup": 2, "doc": 2, "front": 2, "page": 2, "waveguicsx_document": 2, "html": 2, "fulli": 2, "depict": 2, "simpl": 2, "readili": 2, "forget": 2, "consid": [2, 4], "dedic": 2, "develop": 2, "maintain": 2, "universit\u00e9": 2, "dr": 2, "treyss\u00e8d": 2, "contribut": 2, "maximilien": 2, "lehujeur": 2, "manag": 2, "beta": 2, "test": [2, 3], "pierric": 2, "mora": 2, "pleas": [2, 3], "list": [2, 3, 4], "feel": 2, "me": 2, "email": 2, "further": 2, "inform": 2, "question": 2, "about": 2, "project": 2, "academ": 2, "avail": [2, 4], "For": [2, 3, 4], "model": [2, 3], "few": 2, "safe": [2, 3, 4], "l": [2, 3], "laguerr": [2, 3], "numer": 2, "calcul": [2, 4], "lossi": 2, "journal": [2, 3], "acoust": 2, "societi": 2, "america": 2, "133": [2, 3], "2013": [2, 3], "3827": [2, 3], "3837": [2, 3], "nguyen": 2, "hazard": 2, "combin": 2, "sound": 2, "vibrat": 2, "344": 2, "2015": 2, "158": 2, "178": 2, "spectral": [2, 3], "high": [2, 3, 4], "leaki": 2, "solid": 2, "physic": [2, 3], "314": [2, 3], "2016": [2, 3], "341": [2, 3], "354": [2, 3], "gallezot": 2, "approach": [2, 3], "356": 2, "2018": 2, "391": 2, "409": 2, "freeli": 2, "gpl": 2, "visco": 3, "boundari": 3, "condit": 3, "steel": 3, "eigenproblem": [3, 4], "introduc": 3, "imaginari": [3, 4], "neg": [3, 4], "compar": 3, "euler": 3, "bernoulli": 3, "lowest": 3, "whose": 3, "partial": 3, "differenti": 3, "equat": 3, "lesser": 3, "fenicsproject": 3, "formul": 3, "_3": 3, "ufl": 3, "pyvista": 3, "proper": [3, 4], "set_jupyter_backend": 3, "static": 3, "start_xvfb": 3, "try": [3, 4], "none": [3, 4], "pythreej": 3, "ipyvtklink": 3, "7e": 3, "half": 3, "one": [3, 4], "side": 3, "cl": 3, "7932": 3, "5960": 3, "core": 3, "longitudin": 3, "kappa": 3, "kappal": 3, "008": 3, "003": 3, "bulk": 3, "attenu": [3, 4], "wavelength": 3, "linspac": [3, 4], "500e3": 3, "num": 3, "re": [3, 4], "scale": [3, 4], "l0": 3, "t0": 3, "m0": 3, "1j": [3, 4], "creat": [3, 4], "mesh": [3, 4], "six": 3, "node": 3, "triangl": 3, "per": 3, "displac": [3, 4], "create_rectangl": 3, "celltyp": 3, "vectorel": 3, "cg": 3, "lagrang": 3, "quadrat": 3, "p2": 3, "fem": 3, "functionspac": 3, "isotrop": 3, "def": 3, "isotropic_law": 3, "nu": 3, "c11": 3, "c22": 3, "c33": 3, "c12": 3, "c13": 3, "c23": 3, "c44": 3, "c55": 3, "c66": 3, "return": 3, "constant": [3, 4], "scalartyp": 3, "dirichlet": [3, 4], "bc": [3, 4], "fdim": 3, "topologi": 3, "boundary_facet": 3, "locate_entities_boundari": 3, "marker": [3, 4], "lambda": [3, 4], "logical_or": 3, "isclos": 3, "boundary_dof": 3, "locate_dofs_topolog": 3, "entity_dim": 3, "entiti": 3, "dirichletbc": 3, "zero": [3, 4], "dtype": 3, "variat": 3, "trialfunct": 3, "testfunct": 3, "lxy": 3, "as_vector": 3, "dx": 3, "lz": 3, "inner": 3, "k0_form": 3, "form": [3, 4], "k1_form": 3, "k2_form": 3, "mass_form": 3, "assemble_matrix": 3, "diagon": 3, "assembl": 3, "ik": [3, 4], "comparison": 3, "print": 3, "around": [3, 4], "decim": 3, "mu": 3, "ip": [3, 4], "12": 3, "1406": 3, "beam": 3, "accur": 3, "low": [3, 4], "bend": 3, "sqrt": [3, 4], "torsion": 3, "block": 3, "w": 3, "visual": [3, 4], "grid": 3, "unstructuredgrid": 3, "create_vtk_mesh": 3, "plotter": 3, "add_mesh": 3, "show_edg": 3, "true": [3, 4], "view_xi": 3, "getcolumnvector": [3, 4], "u_grid": 3, "reshap": 3, "int": [3, 4], "value_shap": 3, "equal": [3, 4], "u_plott": 3, "style": 3, "wirefram": 3, "warp_by_vector": 3, "factor": [3, 4], "opac": 3, "show_scalar_bar": 3, "edg": 3, "higher": 3, "show_ax": 3, "1d": [3, 4], "remind": [3, 4], "execut": 3, "mpiexec": 3, "elastic_waveguide_squarebar3d_parallelizedloop": 3, "comm_self": 3, "comm": [3, 4], "get_siz": 3, "processor": 3, "rank": 3, "get_rank": 3, "within": 3, "split": 3, "scatter": 3, "param_split": 3, "array_split": 3, "param": 3, "roughli": [3, 4], "els": 3, "param_loc": 3, "gather": 3, "op": 3, "bracket": 3, "traveling_direct": [3, 4], "don": 3, "cannot": [3, 4], "pickl": 3, "keep": 3, "rather": 3, "than": [3, 4], "concaten": 3, "savefig": 3, "elastic_waveguide_bar3d_parallelizedloop": 3, "svg": 3, "cement": 3, "grout": 3, "ha": [3, 4], "parabol": 3, "profil": 3, "fig": 3, "paper": 3, "b": 3, "24": 3, "is_integ": 3, "rais": 3, "notimplementederror": 3, "integ": [3, 4], "adjust": [3, 4], "rho_cor": 3, "cs_core": 3, "cl_core": 3, "kappas_cor": 3, "kappal_cor": 3, "rho_ext": 3, "cs_ext": 3, "cl_ext": 3, "1600": 3, "1700": 3, "2810": 3, "kappas_ext": 3, "kappal_ext": 3, "043": 3, "alpha": [3, 4], "4j": 3, "averag": [3, 4], "absorb": 3, "26e6": 3, "1e3": 3, "200": [3, 4], "find": 3, "exterior": 3, "quadrilater": 3, "gll": 3, "basix": 3, "create_el": 3, "elementfamili": 3, "p": [3, 4], "lagrangevari": 3, "gll_warp": 3, "ufl_wrapp": 3, "basixel": 3, "discontinu": 3, "between": 3, "triu_indic": 3, "upper": 3, "21": 3, "append": 3, "15": 3, "36": 3, "column": 3, "complet": 3, "up": [3, 4], "error": [3, 4], "c_core": 3, "c_ext": 3, "eval_c": 3, "logical_and": 3, "q": [3, 4], "tensorel": 3, "dg": 3, "symmetri": 3, "interpol": 3, "getsiz": 3, "cell": 3, "same": [3, 4], "eval_rho": 3, "vizual": 3, "window_s": 3, "600": 3, "400": 3, "cell_data": 3, "cij": 3, "being": 3, "check": [3, 4], "set_active_scalar": 3, "add_text": 3, "upper_edg": 3, "black": 3, "font_siz": 3, "cartesian": 3, "continu": [3, 4], "eval_gamma": 3, "gammax": 3, "gammai": 3, "gamma": 3, "800": 3, "gridx": 3, "point_data": 3, "subplot": 3, "im": [3, 4], "gridi": 3, "lx": 3, "ly": 3, "evp": [3, 4], "setwhicheigenpair": 3, "pep": [3, 4], "target_magnitud": 3, "prefer": 3, "target_imaginari": 3, "set_xlim": 3, "26": 3, "halfwidth": 3, "686": 3, "db": 3, "2000": 3, "le": 3, "cell_typ": 3, "2nd": 3, "8th": 3, "gauss": 3, "lobatto": 3, "legendr": 3, "like": 3, "geo": 3, "addpoint": 3, "addlin": 3, "addcurveloop": 3, "addplanesurfac": 3, "synchron": 3, "cad": 3, "addphysicalgroup": 3, "tag": 3, "recombin": 3, "setord": 3, "geometri": 3, "cell_tag": 3, "facet_tag": 3, "gmshio": 3, "model_to_mesh": 3, "gdim": 3, "read": 3, "write": 3, "elastic_waveguide_bar3d_open": 3, "msh": 3, "disk": 3, "read_from_msh": 3, "when": [3, 4], "done": [3, 4], "api": 3, "space": 3, "vmesh": 3, "finiteel": 3, "properli": 3, "grid_nod": 3, "add": [3, 4], "render_points_as_spher": 3, "point_siz": 3, "tile": 3, "len": [3, 4], "gridc": 3, "35": 3, "vertic": 3, "ow": 3, "outer": 3, "metadata": 3, "quadrature_rul": 3, "quadrature_degre": 3, "_diag": 3, "getdiagon": 3, "alloc": 3, "memori": 3, "buried_bar_energy_veloc": 3, "png": 3, "buried_bar_attenu": 3, "345": 3, "1f": 3, "2f": 3, "cmap": 3, "viridi": 3, "screenshot": 3, "buried_bar_mode_shap": 3, "transparent_background": 3, "circl": 3, "center": 3, "ot": 3, "motion": 3, "87": 3, "2019": 3, "75": [3, 4], "91": 3, "radiu": 3, "3296": 3, "5963": 3, "60": 3, "origin": 3, "addcirclearc": 3, "emb": 3, "ensur": 3, "two_sid": [3, 4], "settoler": 3, "tol": 3, "max_it": 3, "definit": [3, 4], "dof_coord": 3, "tabulate_dof_coordin": 3, "x0": 3, "desir": 3, "argmin": 3, "linalg": 3, "axi": [3, 4], "nearest": 3, "f0": [3, 4], "createvecright": 3, "orient": 3, "body_forc": 3, "bodi": 3, "amplitud": [3, 4], "traction": 3, "d": 3, "measur": 3, "f_form": 3, "assemble_vector": 3, "set_yscal": 3, "5e": [3, 4], "get_lin": 3, "set_color": 3, "close": [3, 4], "elastic_waveguide_circularbar3d_forcedresponse_gmsh_parallelizedloop": 3, "300": 3, "omega_split": 3, "omega_loc": 3, "linewidth": [3, 4], "linestyl": [3, 4], "tight_layout": 3, "figure_nam": 3, "lamb": 3, "1st": 3, "5b": 3, "7a": 3, "8a": 3, "jasa": 3, "depth": 3, "6020": 3, "1000e3": 3, "250e3": 3, "coeff": [3, 4], "create_interv": 3, "interv": 3, "1e2": 3, "5e6": 3, "set_figheight": 3, "set_figwidth": 3, "plate_zgv_transi": 3, "multipli": [3, 4], "power": [3, 4], "flow": [3, 4], "watt": [3, 4], "insert": 3, "second": 3, "view_zx": 3, "readm": 3, "view": 3, "60e1": 3, "21kg": 3, "76": 3, "70e": 3, "ident": 3, "70cm2": 3, "7850": 3, "3207": 3, "6001": 3, "5000": 3, "regim": 3, "1e5": 3, "rail60e1_60": 3, "21kg_m": 3, "setnumb": 3, "meshsizefactor": 3, "scalingfactor": 3, "zhang": 3, "et": 3, "al": 3, "mechan": 3, "150": [3, 4], "22": 3, "2021": 3, "25": 3, "6000": 3, "phase": [3, 4], "ge": 3, "structur": [3, 4], "health": 3, "monitor": 3, "1287": 3, "1308": 3, "2022": 3, "12000": 3, "rail_phase_veloc": 3, "49": [3, 4], "9": 3, "grid_interp": 3, "onto": 3, "both": [3, 4], "rail_mode_shap": 3, "_mpi": 4, "02": 4, "intracomm": 4, "commun": 4, "problem_typ": 4, "str": 4, "bool": 4, "left": 4, "right": 4, "eigenpair": 4, "look": 4, "ndarrai": 4, "specifi": 4, "ep": 4, "instanc": 4, "eigenforc": 4, "acc": 4, "opposite_go": 4, "opposit": 4, "group_veloc": 4, "pml_ratio": 4, "filter": 4, "out": 4, "dictionnari": 4, "__init__": 4, "constructor": 4, "type": 4, "repeatedli": 4, "whole": 4, "where": 4, "poynt": 4, "pair": 4, "detect": 4, "travel": 4, "wavenumber_funct": 4, "pml_threshold": 4, "o": 4, "markers": 4, "kwarg": 4, "vp": 4, "ve": 4, "vg": 4, "after": 4, "manual": 4, "eigenvectior": 4, "henc": 4, "ad": 4, "remov": 4, "possibl": 4, "duplic": 4, "small": 4, "shift": 4, "might": 4, "sometim": 4, "prevent": 4, "pivot": 4, "everi": 4, "_poynting_norm": 4, "mandatori": 4, "total": 4, "criteria": 4, "unpair": 4, "um": 4, "fm": 4, "denot": 4, "_biorthogonality_factor": 4, "algorithm": 4, "magnitud": 4, "allow": 4, "probabl": 4, "mean": 4, "lack": 4, "nan": 4, "discard": 4, "mani": 4, "occur": 4, "recomput": 4, "increas": 4, "accuraci": 4, "toler": 4, "multipl": 4, "unstructur": 4, "biorthogonol": 4, "specif": 4, "anoth": 4, "flexur": 4, "cylind": 4, "dk": 4, "domega": 4, "delta": 4, "sign": 4, "criterion": 4, "absorpt": 4, "principl": 4, "ek": 4, "kinet": 4, "tend": 4, "vanish": 4, "qm": 4, "exp": 4, "km": 4, "assumpt": 4, "centr": 4, "twice": 4, "sin": 4, "omega_index": 4, "expans": 4, "output": 4, "except": 4, "scalar": 4, "singl": 4, "field": 4, "made": 4, "assum": 4, "valid": 4, "li": 4, "oustid": 4, "region": 4, "consider": 4, "replac": 4, "string": 4, "respect": 4, "phase_veloc": 4, "angl": 4, "etc": 4, "float": 4, "threshold": 4, "pass": 4, "collect": 4, "alreadi": 4, "scaler": 4, "dimens": 4, "meter": 4, "newton": 4, "self": 4, "displai": 4, "waveform": 4, "sampl": 4, "least": 4, "highest": 4, "2fmax": 4, "larg": 4, "enough": 4, "captur": 4, "slowest": 4, "distanc": 4, "fourier": 4, "integr": 4, "dt": 4, "remark": 4, "convent": 4, "obtain": 4, "abov": 4, "divis": 4, "simplifi": 4, "dimensionless": 4, "analys": 4, "nonzero": 4, "analysi": 4, "long": 4, "avoid": 4, "alias": 4, "good": 4, "choic": 4, "kept": 4, "mysign": 4, "5000e3": 4, "nd": 4, "stack": 4, "row": 4, "number_of_sign": 4, "decai": 4, "f1": 4, "chirp_dur": 4, "step": 4, "odd": 4, "suppress": 4, "pad": 4, "miss": 4, "greater": 4, "max": 4, "wavelet": 4, "better": 4, "round": 4, "han": 4, "sinusoid": 4, "fmax": 4, "choos": 4, "4fc": 4, "last": 4, "sweep": 4}, "objects": {"": [[4, 0, 0, "-", "waveguicsx"]], "waveguicsx": [[4, 0, 0, "-", "waveguide"]], "waveguicsx.waveguide": [[4, 1, 1, "", "Signal"], [4, 1, 1, "", "Waveguide"]], "waveguicsx.waveguide.Signal": [[4, 2, 1, "", "chirp"], [4, 2, 1, "", "fft"], [4, 2, 1, "", "ifft"], [4, 2, 1, "", "plot"], [4, 2, 1, "", "plot_spectrum"], [4, 2, 1, "", "ricker"], [4, 2, 1, "", "toneburst"]], "waveguicsx.waveguide.Waveguide": [[4, 2, 1, "", "compute_eigenforces"], [4, 2, 1, "", "compute_energy_velocity"], [4, 2, 1, "", "compute_group_velocity"], [4, 2, 1, "", "compute_opposite_going"], [4, 2, 1, "", "compute_pml_ratio"], [4, 2, 1, "", "compute_poynting_normalization"], [4, 2, 1, "", "compute_response"], [4, 2, 1, "", "compute_response_coefficient"], [4, 2, 1, "", "compute_traveling_direction"], [4, 2, 1, "", "plot"], [4, 2, 1, "", "plot_attenuation"], [4, 2, 1, "", "plot_coefficient"], [4, 2, 1, "", "plot_energy_velocity"], [4, 2, 1, "", "plot_excitability"], [4, 2, 1, "", "plot_group_velocity"], [4, 2, 1, "", "plot_phase_velocity"], [4, 2, 1, "", "plot_spectrum"], [4, 2, 1, "", "set_parameters"], [4, 2, 1, "", "set_plot_scaler"], [4, 2, 1, "", "solve"]]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:method"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "method", "Python method"]}, "titleterms": {"document": [0, 2], "waveguicsx": [1, 2, 4], "version": 1, "1": [1, 2], "content": 1, "present": 2, "0": 2, "introduct": 2, "basic": 2, "exampl": 2, "2": 2, "prerequisit": 2, "3": 2, "instal": 2, "clone": 2, "public": 2, "repositori": 2, "gener": 2, "docker": 2, "imag": 2, "run": 2, "contain": 2, "us": [2, 3], "4": 2, "5": 2, "tutori": [2, 3], "6": 2, "author": 2, "contributor": 2, "7": 2, "how": 2, "cite": 2, "8": 2, "licens": 2, "3d": 3, "elast": 3, "bar": 3, "squar": 3, "cross": 3, "section": 3, "parallel": 3, "buri": 3, "pml": 3, "extern": 3, "medium": 3, "gmsh": 3, "excit": 3, "circular": 3, "time": 3, "respons": 3, "plate": 3, "its": 3, "first": 3, "zgv": 3, "reson": 3, "dispers": 3, "curv": 3, "rail": 3, "waveguid": 4, "attribut": 4, "method": 4, "paramet": 4, "return": 4}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 8, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.todo": 2, "sphinx.ext.viewcode": 1, "nbsphinx": 4, "sphinx": 57}, "alltitles": {"Documentation": [[0, "documentation"]], "Waveguicsx - Version 1.1": [[1, "waveguicsx-version-version"]], "Contents:": [[1, null]], "Presentation": [[2, "presentation"]], "0. Introduction": [[2, "introduction"]], "1. Basic examples": [[2, "basic-examples"]], "2. Prerequisites": [[2, "prerequisites"]], "3. Installation": [[2, "installation"]], "3.1 Clone the Waveguicsx public repository": [[2, "clone-the-waveguicsx-public-repository"]], "3.2 Generate the docker image and run the container using :": [[2, "generate-the-docker-image-and-run-the-container-using"]], "4. Documentation": [[2, "documentation"]], "5. Tutorials": [[2, "tutorials"]], "6. Authors and contributors": [[2, "authors-and-contributors"]], "7. How to cite": [[2, "how-to-cite"]], "8. License": [[2, "license"]], "Tutorials": [[3, "tutorials"]], "3D elastic bar of square cross-section": [[3, "d-elastic-bar-of-square-cross-section"]], "3D elastic bar of square cross-section with parallelization": [[3, "d-elastic-bar-of-square-cross-section-with-parallelization"]], "3D elastic bar of square cross-section buried into a PML external medium": [[3, "d-elastic-bar-of-square-cross-section-buried-into-a-pml-external-medium"]], "3D elastic bar of square cross-section buried into a PML external medium using gmsh": [[3, "d-elastic-bar-of-square-cross-section-buried-into-a-pml-external-medium-using-gmsh"]], "Excitation of a 3D elastic bar of circular cross-section": [[3, "excitation-of-a-3d-elastic-bar-of-circular-cross-section"]], "Excitation of a 3D elastic bar of circular cross-section with parallelization": [[3, "excitation-of-a-3d-elastic-bar-of-circular-cross-section-with-parallelization"]], "Time response of a plate excited near its first ZGV resonance": [[3, "time-response-of-a-plate-excited-near-its-first-zgv-resonance"]], "Dispersion curves of a rail": [[3, "dispersion-curves-of-a-rail"]], "waveguicsx.waveguide": [[4, "module-waveguicsx"]], "Attributes": [[4, "attributes"], [4, "id32"]], "Methods": [[4, "methods"], [4, "id33"]], "Parameters": [[4, "parameters"], [4, "id19"], [4, "id22"], [4, "id23"], [4, "id24"], [4, "id28"]], "Returns": [[4, "returns"], [4, "id27"], [4, "id31"]]}, "indexentries": {"signal (class in waveguicsx.waveguide)": [[4, "waveguicsx.waveguide.Signal"]], "waveguide (class in waveguicsx.waveguide)": [[4, "waveguicsx.waveguide.Waveguide"]], "chirp() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.chirp"]], "compute_eigenforces() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_eigenforces"]], "compute_energy_velocity() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_energy_velocity"]], "compute_group_velocity() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_group_velocity"]], "compute_opposite_going() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_opposite_going"]], "compute_pml_ratio() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_pml_ratio"]], "compute_poynting_normalization() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_poynting_normalization"]], "compute_response() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_response"]], "compute_response_coefficient() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_response_coefficient"]], "compute_traveling_direction() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_traveling_direction"]], "fft() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.fft"]], "ifft() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.ifft"]], "module": [[4, "module-waveguicsx"], [4, "module-waveguicsx.waveguide"]], "plot() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.plot"]], "plot() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot"]], "plot_attenuation() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_attenuation"]], "plot_coefficient() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_coefficient"]], "plot_energy_velocity() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_energy_velocity"]], "plot_excitability() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_excitability"]], "plot_group_velocity() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_group_velocity"]], "plot_phase_velocity() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_phase_velocity"]], "plot_spectrum() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.plot_spectrum"]], "plot_spectrum() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_spectrum"]], "ricker() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.ricker"]], "set_parameters() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.set_parameters"]], "set_plot_scaler() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.set_plot_scaler"]], "solve() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.solve"]], "toneburst() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.toneburst"]], "waveguicsx": [[4, "module-waveguicsx"]], "waveguicsx.waveguide": [[4, "module-waveguicsx.waveguide"]]}}) \ No newline at end of file +Search.setIndex({"docnames": ["documentation", "index", "readme_link", "tutorials", "waveguicsx"], "filenames": ["documentation.rst", "index.rst", "readme_link.md", "tutorials.rst", "waveguicsx.rst"], "titles": ["Documentation", "Waveguicsx - Version 1.1", "Presentation", "Tutorials", "waveguicsx.waveguide"], "terms": {"waveguicsx": [0, 3], "waveguid": [0, 1, 2, 3], "set_paramet": [0, 1, 2, 3, 4], "solv": [0, 1, 2, 3, 4], "compute_eigenforc": [0, 1, 4], "compute_poynting_norm": [0, 1, 4], "compute_energy_veloc": [0, 1, 2, 3, 4], "compute_opposite_go": [0, 1, 4], "compute_group_veloc": [0, 1, 2, 4], "compute_traveling_direct": [0, 1, 3, 4], "compute_pml_ratio": [0, 1, 4], "compute_response_coeffici": [0, 1, 2, 3, 4], "compute_respons": [0, 1, 2, 3, 4], "plot_phase_veloc": [0, 1, 3, 4], "plot_attenu": [0, 1, 3, 4], "plot_energy_veloc": [0, 1, 2, 3, 4], "plot_group_veloc": [0, 1, 3, 4], "plot_coeffici": [0, 1, 3, 4], "plot_excit": [0, 1, 3, 4], "plot": [0, 1, 2, 3, 4], "set_plot_scal": [0, 1, 2, 3, 4], "plot_spectrum": [0, 1, 2, 3, 4], "signal": [0, 1, 2, 3, 4], "fft": [0, 1, 2, 4], "ifft": [0, 1, 2, 3, 4], "ricker": [0, 1, 4], "toneburst": [0, 1, 2, 3, 4], "chirp": [0, 1, 4], "1": [0, 4], "present": 1, "0": [1, 4], "introduct": 1, "basic": [1, 3], "exampl": [1, 3, 4], "2": [1, 4], "prerequisit": 1, "3": [1, 4], "instal": 1, "clone": 1, "public": [1, 3], "repositori": 1, "gener": [1, 3, 4], "docker": 1, "imag": [1, 3, 4], "run": 1, "contain": [1, 3, 4], "us": [1, 4], "4": [1, 4], "document": [1, 3, 4], "5": [1, 4], "tutori": [1, 4], "6": 1, "author": 1, "contributor": 1, "7": 1, "how": 1, "cite": 1, "8": [1, 3, 4], "licens": [1, 3], "3d": [3, 4], "elast": [1, 2], "bar": [1, 2], "squar": 1, "cross": [1, 2, 4], "section": [1, 2, 4], "parallel": [1, 2, 4], "buri": [1, 2, 4], "pml": [1, 2, 4], "extern": 1, "medium": 1, "gmsh": 1, "excit": [1, 2, 4], "circular": 1, "time": [1, 2, 4], "respons": [1, 2, 4], "plate": [1, 2, 4], "its": 1, "first": [1, 2, 4], "zgv": 1, "reson": 1, "dispers": [1, 2, 4], "curv": [1, 2, 4], "rail": [1, 2], "python": [2, 3, 4], "librari": [2, 3], "complex": [2, 3, 4], "problem": [2, 3, 4], "copyright": [2, 3], "c": [2, 3, 4], "2023": [2, 3], "2024": [2, 3], "fabien": [2, 3], "treyssed": [2, 3], "thi": [2, 3, 4], "file": [2, 3], "i": [2, 3, 4], "part": [2, 3, 4], "free": [2, 3, 4], "softwar": [2, 3], "you": 2, "can": [2, 3, 4], "redistribut": 2, "modifi": 2, "under": [2, 3], "term": [2, 4], "gnu": [2, 3], "publish": 2, "foundat": 2, "either": [2, 4], "version": 2, "your": 2, "option": [2, 4], "ani": [2, 4], "later": 2, "distribut": [2, 3], "hope": 2, "without": 2, "warranti": 2, "even": [2, 4], "impli": 2, "merchant": 2, "fit": 2, "FOR": 2, "A": [2, 4], "particular": 2, "purpos": 2, "see": [2, 3, 4], "more": [2, 3], "detail": 2, "should": [2, 3, 4], "have": 2, "receiv": [2, 4], "copi": 2, "along": [2, 3, 4], "If": [2, 4], "http": [2, 3], "www": 2, "org": [2, 3], "contact": 2, "univ": 2, "eiffel": 2, "fr": 2, "base": [2, 4], "slepc": [2, 3, 4], "eigensolv": [2, 4], "The": [2, 3, 4], "full": [2, 3, 4], "entir": [2, 3, 4], "defin": [2, 3, 4], "py": [2, 3, 4], "modul": [2, 3, 4], "deal": [2, 4], "two": [1, 2, 4], "dimension": [1, 2, 4], "e": [2, 3, 4], "g": [2, 3, 4], "three": [1, 2, 4], "arbitrarili": [2, 4], "shape": [2, 3, 4], "inhomogen": [2, 4], "transvers": [2, 4], "direct": [2, 3, 4], "anisotrop": [2, 4], "valu": [2, 3, 4], "handl": [2, 3, 4], "includ": [2, 3, 4], "effect": [2, 4], "non": [2, 4], "propag": [2, 3, 4], "mode": [2, 3, 4], "evanesc": [2, 4], "viscoelast": [2, 3, 4], "loss": [2, 3, 4], "materi": [2, 3, 4], "properti": [2, 3, 4], "perfectli": [2, 4], "match": [2, 4], "layer": [2, 4], "simul": [2, 4], "precis": 2, "follow": [2, 3, 4], "matrix": [2, 3, 4], "textbf": [2, 3], "k": [2, 3, 4], "_0": 2, "omega": [2, 3, 4], "m": [2, 3, 4], "text": [2, 3], "_1": [2, 3], "t": [2, 3, 4], "_2": [2, 3], "u": [2, 3, 4], "f": [2, 3, 4], "kind": [2, 4], "typic": [2, 4], "stem": [2, 4], "from": [2, 3, 4], "so": [2, 4], "call": [2, 3, 4], "semi": [2, 4], "analyt": [2, 3, 4], "finit": [2, 3, 4], "element": [2, 3, 4], "fe": [2, 3], "method": [2, 3], "refer": 2, "below": [2, 3], "theoret": [2, 4], "input": [2, 3, 4], "ar": [2, 3, 4], "matric": [2, 3, 4], "petsc": [2, 3, 4], "format": 2, "comput": [2, 3, 4], "well": [2, 4], "vector": [2, 3, 4], "forc": [2, 3, 4], "requir": [2, 4], "These": 2, "built": [2, 3], "own": 2, "favorit": 2, "code": 2, "In": [2, 3, 4], "case": [2, 3, 4], "just": 2, "need": [2, 4], "import": [2, 3, 4], "convert": 2, "them": [2, 3, 4], "do": [2, 3], "open": [2, 3], "fenicsx": [2, 3], "shown": [2, 4], "correspond": [2, 4], "an": [2, 3, 4], "eigenvalu": [2, 3, 4], "iter": [2, 4], "vari": [2, 3, 4], "paramet": [2, 3], "which": [2, 3, 4], "angular": [2, 3, 4], "frequenc": [2, 3, 4], "wavenumb": [2, 3, 4], "lead": [2, 3, 4], "result": [2, 3, 4], "former": [2, 4], "while": [2, 4], "latter": [2, 4], "loop": [2, 3, 4], "over": [2, 4], "some": [2, 3, 4], "mpi4pi": [2, 3, 4], "variou": [2, 4], "modal": [2, 4], "energi": [2, 3, 4], "veloc": [2, 3, 4], "group": [2, 3, 4], "post": [2, 3, 4], "process": [2, 3, 4], "function": [2, 3, 4], "repons": [2, 4], "neq": 2, "domain": [2, 3, 4], "expand": [2, 4], "solut": [2, 3, 4], "sum": [2, 3, 4], "eigenmod": [2, 3, 4], "biorthogon": [2, 4], "relationship": [2, 4], "veri": [2, 4], "fast": [2, 4], "wavefield": [2, 4], "transient": [2, 4], "final": [2, 3], "invers": [2, 4], "class": [2, 4], "main": 2, "enabl": [2, 3, 4], "other": 2, "provid": 2, "easili": 2, "transform": [2, 3, 4], "puls": 2, "homogen": 2, "note": [2, 3, 4], "previous": 2, "store": [2, 3, 4], "binari": 2, "want": 2, "convers": 2, "given": [2, 4], "2d": [2, 3, 4], "numpi": [2, 3, 4], "arrai": [2, 3, 4], "dens": 2, "mat": [2, 4], "createdens": 2, "spars": 2, "matlab": 2, "scipi": 2, "io": [2, 3], "loadmat": 2, "matlab_fil": 2, "here": [2, 3, 4], "suppos": 2, "variabl": [2, 4], "name": [2, 4], "tocsr": 2, "csr": 2, "createaij": 2, "size": [2, 3, 4], "indptr": 2, "indic": 2, "data": [2, 4], "mpi": [2, 3, 4], "petsc4pi": [2, 3, 4], "np": [2, 3, 4], "matplotlib": [2, 3, 4], "pyplot": [2, 3], "plt": [2, 3, 4], "load": 2, "k0": [2, 3, 4], "k1": [2, 3, 4], "k2": [2, 3, 4], "save": [2, 3], "basicexample_k0k1k2mf": [2, 3], "dat": [2, 3], "thick": [2, 3], "poisson": 2, "ratio": [2, 3, 4], "It": 2, "found": [2, 4], "subfold": 2, "elastic_waveguide_plate2d_transientrespons": 2, "viewer": [2, 3], "createbinari": [2, 3], "r": 2, "must": [2, 3, 4], "order": [2, 3, 4], "object": [2, 3, 4], "been": [2, 4], "initi": [2, 3, 4], "wg": [2, 3, 4], "comm_world": [2, 3, 4], "arang": [2, 3], "10": [2, 3], "set": [2, 3, 4], "rang": [2, 3, 4], "normal": [2, 3, 4], "uncom": [2, 3], "line": [2, 3], "instead": [2, 3, 4], "reduc": [2, 3, 4], "also": [2, 3, 4], "nev": [2, 3, 4], "20": [2, 3], "target": [2, 3, 4], "access": [2, 3, 4], "eigensolut": [2, 4], "iomega": [2, 4], "imod": [2, 3, 4], "eigenvector": [2, 3, 4], "idof": [2, 3, 4], "default": [2, 4], "v": [2, 3, 4], "show": [2, 3, 4], "h": [2, 3, 4], "rho": [2, 3], "01": [2, 3, 4], "3260": [2, 3], "7800": [2, 3], "shear": [2, 3], "wave": [2, 3, 4], "celer": [2, 3], "": [2, 3], "densiti": [2, 3], "kg": [2, 3], "length": [2, 3, 4], "mass": [2, 3, 4], "dim": [2, 3, 4], "characterist": [2, 3, 4], "hz": [2, 3, 4], "user": [2, 4], "unit": [2, 3, 4], "mhz": [2, 3], "mm": [2, 3], "plot_scal": [2, 3, 4], "energy_veloc": [2, 3, 4], "1000": [2, 3], "sc": [2, 3, 4], "posit": [2, 4], "go": [2, 4], "ax": [2, 3, 4], "set_xlabel": [2, 3], "set_ylabel": [2, 3], "vec": [2, 3, 4], "3218": [2, 3], "m3": [2, 3], "number": [2, 3, 4], "request": [2, 4], "each": [2, 3, 4], "spectrum": [2, 3, 4], "400e3": 2, "2e": [2, 3], "fc": [2, 3, 4], "100e3": [2, 4], "n": [2, 3, 4], "central": [2, 4], "100": [2, 3, 4], "khz": [2, 3], "cycl": [2, 4], "durat": [2, 4], "pi": [2, 3, 4], "rad": [2, 3], "becaus": [2, 4], "otherwis": [2, 3, 4], "comment": 2, "coeffici": [2, 3, 4], "due": [2, 4], "degre": [2, 4], "freedom": [2, 4], "dof": [2, 3, 4], "point": [2, 3, 4], "appli": [2, 4], "bottom": 2, "surfac": [2, 3], "38": 2, "index": [2, 3, 4], "x": [2, 3, 4], "compon": [2, 3, 4], "top": 2, "ab": [2, 3, 4], "norm": [2, 3], "log": [2, 3, 4], "color": [2, 3, 4], "modulu": 2, "colorbar": 2, "set_label": 2, "set_ylim": [2, 3], "y": [2, 3, 4], "limit": [2, 4], "necessari": [2, 3], "set_clim": 2, "1e": [2, 3], "14": 2, "11": [2, 3], "axial": [2, 4], "coordin": [2, 3, 4], "z": [2, 3, 4], "50": [2, 3, 4], "fals": [2, 3, 4], "slepc4pi": [2, 3], "platform": [2, 3], "To": 2, "therefor": 2, "thei": [2, 3, 4], "conveni": 2, "editor": 2, "jupyt": [2, 3], "notebook": [2, 3], "current": 2, "written": 2, "v0": 2, "minor": 2, "modif": 2, "mai": [2, 4], "latest": 2, "work": [2, 3], "progress": 2, "git": 2, "github": [2, 3], "com": [2, 3], "universit": 2, "gustav": 2, "cd": 2, "depend": 2, "nevertheless": 2, "we": 2, "recommend": 2, "dolfinx": [2, 3], "sourc": [2, 3, 4], "befor": [2, 4], "root": [2, 3], "Then": 2, "shell": 2, "script": 2, "launch_fenicsx": 2, "sh": 2, "insid": [2, 3, 4], "take": 2, "onc": 2, "launch": 2, "packag": 2, "onli": [2, 3, 4], "waveguicsxus": 2, "hostnam": 2, "python3": [2, 3], "pip": 2, "warn": [2, 4], "folder": 2, "mount": 2, "home": 2, "all": [2, 3, 4], "chang": [2, 4], "persist": 2, "host": 2, "system": [2, 3], "local": [2, 3, 4], "ignor": 2, "reinstal": 2, "usag": [2, 3], "real": [2, 3, 4], "back": 2, "elastic_waveguide_squarebar3d": 2, "cli": 2, "exit": 2, "leav": 2, "build": [2, 3], "setup": 2, "doc": 2, "front": 2, "page": 2, "waveguicsx_document": 2, "html": 2, "fulli": 2, "depict": 2, "simpl": 2, "readili": 2, "forget": 2, "consid": [2, 4], "dedic": 2, "develop": 2, "maintain": 2, "universit\u00e9": 2, "dr": 2, "treyss\u00e8d": 2, "contribut": 2, "maximilien": 2, "lehujeur": 2, "manag": 2, "beta": 2, "test": [2, 3], "pierric": 2, "mora": 2, "pleas": [2, 3], "list": [2, 3, 4], "feel": 2, "me": 2, "email": 2, "further": 2, "inform": 2, "question": 2, "about": 2, "project": 2, "academ": 2, "avail": [2, 4], "For": [2, 3, 4], "model": [2, 3], "few": 2, "safe": [2, 3, 4], "l": [2, 3], "laguerr": [2, 3], "numer": 2, "calcul": [2, 4], "lossi": 2, "journal": [2, 3], "acoust": 2, "societi": 2, "america": 2, "133": [2, 3], "2013": [2, 3], "3827": [2, 3], "3837": [2, 3], "nguyen": 2, "hazard": 2, "combin": 2, "sound": 2, "vibrat": 2, "344": 2, "2015": 2, "158": 2, "178": 2, "spectral": [2, 3], "high": [2, 3, 4], "leaki": 2, "solid": 2, "physic": [2, 3], "314": [2, 3], "2016": [2, 3], "341": [2, 3], "354": [2, 3], "gallezot": 2, "approach": [2, 3], "356": 2, "2018": 2, "391": 2, "409": 2, "freeli": 2, "gpl": 2, "visco": 3, "boundari": 3, "condit": 3, "steel": 3, "eigenproblem": [3, 4], "introduc": 3, "imaginari": [3, 4], "neg": [3, 4], "compar": 3, "euler": 3, "bernoulli": 3, "lowest": 3, "whose": 3, "partial": 3, "differenti": 3, "equat": 3, "lesser": 3, "fenicsproject": 3, "formul": 3, "_3": 3, "ufl": 3, "pyvista": 3, "proper": [3, 4], "set_jupyter_backend": 3, "static": 3, "start_xvfb": 3, "try": [3, 4], "none": [3, 4], "pythreej": 3, "ipyvtklink": 3, "7e": 3, "half": 3, "one": [3, 4], "side": 3, "cl": 3, "7932": 3, "5960": 3, "core": 3, "longitudin": 3, "kappa": 3, "kappal": 3, "008": 3, "003": 3, "bulk": 3, "attenu": [3, 4], "wavelength": 3, "linspac": [3, 4], "500e3": 3, "num": 3, "re": [3, 4], "scale": [3, 4], "l0": 3, "t0": 3, "m0": 3, "1j": [3, 4], "creat": [3, 4], "mesh": [3, 4], "six": 3, "node": 3, "triangl": 3, "per": 3, "displac": [3, 4], "create_rectangl": 3, "celltyp": 3, "vectorel": 3, "cg": 3, "lagrang": 3, "quadrat": 3, "p2": 3, "fem": 3, "functionspac": 3, "isotrop": 3, "def": 3, "isotropic_law": 3, "nu": 3, "c11": 3, "c22": 3, "c33": 3, "c12": 3, "c13": 3, "c23": 3, "c44": 3, "c55": 3, "c66": 3, "return": 3, "constant": [3, 4], "scalartyp": 3, "dirichlet": [3, 4], "bc": [3, 4], "fdim": 3, "topologi": 3, "boundary_facet": 3, "locate_entities_boundari": 3, "marker": [3, 4], "lambda": [3, 4], "logical_or": 3, "isclos": 3, "boundary_dof": 3, "locate_dofs_topolog": 3, "entity_dim": 3, "entiti": 3, "dirichletbc": 3, "zero": [3, 4], "dtype": 3, "variat": 3, "trialfunct": 3, "testfunct": 3, "lxy": 3, "as_vector": 3, "dx": 3, "lz": 3, "inner": 3, "k0_form": 3, "form": [3, 4], "k1_form": 3, "k2_form": 3, "mass_form": 3, "assemble_matrix": 3, "diagon": 3, "assembl": 3, "ik": [3, 4], "comparison": 3, "print": 3, "around": [3, 4], "decim": 3, "mu": 3, "ip": [3, 4], "12": 3, "1406": 3, "beam": 3, "accur": 3, "low": [3, 4], "bend": 3, "sqrt": [3, 4], "torsion": 3, "block": 3, "w": 3, "visual": [3, 4], "grid": 3, "unstructuredgrid": 3, "create_vtk_mesh": 3, "plotter": 3, "add_mesh": 3, "show_edg": 3, "true": [3, 4], "view_xi": 3, "getcolumnvector": [3, 4], "u_grid": 3, "reshap": 3, "int": [3, 4], "value_shap": 3, "equal": [3, 4], "u_plott": 3, "style": 3, "wirefram": 3, "warp_by_vector": 3, "factor": [3, 4], "opac": 3, "show_scalar_bar": 3, "edg": 3, "higher": 3, "show_ax": 3, "1d": [3, 4], "remind": [3, 4], "execut": 3, "mpiexec": 3, "elastic_waveguide_squarebar3d_parallelizedloop": 3, "comm_self": 3, "comm": [3, 4], "get_siz": 3, "processor": 3, "rank": 3, "get_rank": 3, "within": 3, "split": 3, "scatter": 3, "param_split": 3, "array_split": 3, "param": 3, "roughli": [3, 4], "els": 3, "param_loc": 3, "gather": 3, "op": 3, "bracket": 3, "traveling_direct": [3, 4], "don": 3, "cannot": [3, 4], "pickl": 3, "keep": 3, "rather": 3, "than": [3, 4], "concaten": 3, "savefig": 3, "elastic_waveguide_bar3d_parallelizedloop": 3, "svg": 3, "cement": 3, "grout": 3, "ha": [3, 4], "parabol": 3, "profil": 3, "fig": 3, "paper": 3, "b": 3, "24": 3, "is_integ": 3, "rais": 3, "notimplementederror": 3, "integ": [3, 4], "adjust": [3, 4], "rho_cor": 3, "cs_core": 3, "cl_core": 3, "kappas_cor": 3, "kappal_cor": 3, "rho_ext": 3, "cs_ext": 3, "cl_ext": 3, "1600": 3, "1700": 3, "2810": 3, "kappas_ext": 3, "kappal_ext": 3, "043": 3, "alpha": [3, 4], "4j": 3, "averag": [3, 4], "absorb": 3, "26e6": 3, "1e3": 3, "200": [3, 4], "find": 3, "exterior": 3, "quadrilater": 3, "gll": 3, "basix": 3, "create_el": 3, "elementfamili": 3, "p": [3, 4], "lagrangevari": 3, "gll_warp": 3, "ufl_wrapp": 3, "basixel": 3, "discontinu": 3, "between": 3, "triu_indic": 3, "upper": 3, "21": 3, "append": 3, "15": 3, "36": 3, "column": 3, "complet": 3, "up": [3, 4], "error": [3, 4], "c_core": 3, "c_ext": 3, "eval_c": 3, "logical_and": 3, "q": [3, 4], "tensorel": 3, "dg": 3, "symmetri": 3, "interpol": 3, "getsiz": 3, "cell": 3, "same": [3, 4], "eval_rho": 3, "vizual": 3, "window_s": 3, "600": 3, "400": 3, "cell_data": 3, "cij": 3, "being": 3, "check": [3, 4], "set_active_scalar": 3, "add_text": 3, "upper_edg": 3, "black": 3, "font_siz": 3, "cartesian": 3, "continu": [3, 4], "eval_gamma": 3, "gammax": 3, "gammai": 3, "gamma": 3, "800": 3, "gridx": 3, "point_data": 3, "subplot": 3, "im": [3, 4], "gridi": 3, "lx": 3, "ly": 3, "evp": [3, 4], "setwhicheigenpair": 3, "pep": [3, 4], "target_magnitud": 3, "prefer": 3, "target_imaginari": 3, "set_xlim": 3, "26": 3, "halfwidth": 3, "686": 3, "db": 3, "2000": 3, "le": 3, "cell_typ": 3, "2nd": 3, "8th": 3, "gauss": 3, "lobatto": 3, "legendr": 3, "like": 3, "geo": 3, "addpoint": 3, "addlin": 3, "addcurveloop": 3, "addplanesurfac": 3, "synchron": 3, "cad": 3, "addphysicalgroup": 3, "tag": 3, "recombin": 3, "setord": 3, "geometri": 3, "cell_tag": 3, "facet_tag": 3, "gmshio": 3, "model_to_mesh": 3, "gdim": 3, "read": 3, "write": 3, "elastic_waveguide_bar3d_open": 3, "msh": 3, "disk": 3, "read_from_msh": 3, "when": [3, 4], "done": [3, 4], "api": 3, "space": 3, "vmesh": 3, "finiteel": 3, "properli": 3, "grid_nod": 3, "add": [3, 4], "render_points_as_spher": 3, "point_siz": 3, "tile": 3, "len": [3, 4], "gridc": 3, "35": 3, "vertic": 3, "ow": 3, "outer": 3, "metadata": 3, "quadrature_rul": 3, "quadrature_degre": 3, "_diag": 3, "getdiagon": 3, "alloc": 3, "memori": 3, "buried_bar_energy_veloc": 3, "png": 3, "buried_bar_attenu": 3, "345": 3, "1f": 3, "2f": 3, "cmap": 3, "viridi": 3, "screenshot": 3, "buried_bar_mode_shap": 3, "transparent_background": 3, "circl": 3, "center": 3, "ot": 3, "motion": 3, "87": 3, "2019": 3, "75": [3, 4], "91": 3, "radiu": 3, "3296": 3, "5963": 3, "60": 3, "origin": 3, "addcirclearc": 3, "emb": 3, "ensur": 3, "two_sid": [3, 4], "settoler": 3, "tol": 3, "max_it": 3, "definit": [3, 4], "dof_coord": 3, "tabulate_dof_coordin": 3, "x0": 3, "desir": 3, "argmin": 3, "linalg": 3, "axi": [3, 4], "nearest": 3, "f0": [3, 4], "createvecright": 3, "orient": 3, "body_forc": 3, "bodi": 3, "amplitud": [3, 4], "traction": 3, "d": 3, "measur": 3, "f_form": 3, "assemble_vector": 3, "set_yscal": 3, "5e": [3, 4], "get_lin": 3, "set_color": 3, "close": [3, 4], "elastic_waveguide_circularbar3d_forcedresponse_gmsh_parallelizedloop": 3, "300": 3, "omega_split": 3, "omega_loc": 3, "linewidth": [3, 4], "linestyl": [3, 4], "tight_layout": 3, "figure_nam": 3, "lamb": 3, "1st": 3, "5b": 3, "7a": 3, "8a": 3, "jasa": 3, "depth": 3, "6020": 3, "1000e3": 3, "250e3": 3, "coeff": [3, 4], "create_interv": 3, "interv": 3, "1e2": 3, "5e6": 3, "set_figheight": 3, "set_figwidth": 3, "plate_zgv_transi": 3, "multipli": [3, 4], "power": [3, 4], "flow": [3, 4], "watt": [3, 4], "insert": 3, "second": 3, "view_zx": 3, "readm": 3, "view": 3, "60e1": 3, "21kg": 3, "76": 3, "70e": 3, "ident": 3, "70cm2": 3, "7850": 3, "3207": 3, "6001": 3, "5000": 3, "regim": 3, "1e5": 3, "rail60e1_60": 3, "21kg_m": 3, "setnumb": 3, "meshsizefactor": 3, "scalingfactor": 3, "zhang": 3, "et": 3, "al": 3, "mechan": 3, "150": [3, 4], "22": 3, "2021": 3, "25": 3, "6000": 3, "phase": [3, 4], "ge": 3, "structur": [3, 4], "health": 3, "monitor": 3, "1287": 3, "1308": 3, "2022": 3, "12000": 3, "rail_phase_veloc": 3, "49": [3, 4], "9": 3, "grid_interp": 3, "onto": 3, "both": [3, 4], "rail_mode_shap": 3, "_mpi": 4, "02": 4, "intracomm": 4, "commun": 4, "problem_typ": 4, "str": 4, "bool": 4, "left": 4, "right": 4, "eigenpair": 4, "look": 4, "ndarrai": 4, "specifi": 4, "ep": 4, "instanc": 4, "eigenforc": 4, "acc": 4, "opposite_go": 4, "opposit": 4, "group_veloc": 4, "pml_ratio": 4, "filter": 4, "out": 4, "dictionnari": 4, "__init__": 4, "constructor": 4, "type": 4, "repeatedli": 4, "whole": 4, "where": 4, "poynt": 4, "pair": 4, "detect": 4, "travel": 4, "wavenumber_funct": 4, "pml_threshold": 4, "o": 4, "markers": 4, "kwarg": 4, "vp": 4, "ve": 4, "vg": 4, "after": 4, "manual": 4, "eigenvectior": 4, "henc": 4, "ad": 4, "remov": 4, "possibl": 4, "duplic": 4, "small": 4, "shift": 4, "might": 4, "sometim": 4, "prevent": 4, "pivot": 4, "everi": 4, "_poynting_norm": 4, "mandatori": 4, "total": 4, "criteria": 4, "unpair": 4, "um": 4, "fm": 4, "denot": 4, "_biorthogonality_factor": 4, "algorithm": 4, "magnitud": 4, "allow": 4, "probabl": 4, "mean": 4, "lack": 4, "nan": 4, "discard": 4, "mani": 4, "occur": 4, "recomput": 4, "increas": 4, "accuraci": 4, "toler": 4, "multipl": 4, "unstructur": 4, "biorthogonol": 4, "specif": 4, "anoth": 4, "flexur": 4, "cylind": 4, "dk": 4, "domega": 4, "delta": 4, "sign": 4, "criterion": 4, "absorpt": 4, "principl": 4, "ek": 4, "kinet": 4, "tend": 4, "vanish": 4, "qm": 4, "exp": 4, "km": 4, "assumpt": 4, "centr": 4, "twice": 4, "sin": 4, "omega_index": 4, "expans": 4, "output": 4, "except": 4, "scalar": 4, "singl": 4, "field": 4, "made": 4, "assum": 4, "valid": 4, "li": 4, "oustid": 4, "region": 4, "consider": 4, "replac": 4, "string": 4, "respect": 4, "phase_veloc": 4, "angl": 4, "etc": 4, "float": 4, "threshold": 4, "pass": 4, "collect": 4, "alreadi": 4, "scaler": 4, "dimens": 4, "meter": 4, "newton": 4, "self": 4, "displai": 4, "waveform": 4, "sampl": 4, "least": 4, "highest": 4, "2fmax": 4, "larg": 4, "enough": 4, "captur": 4, "slowest": 4, "distanc": 4, "fourier": 4, "integr": 4, "dt": 4, "remark": 4, "convent": 4, "obtain": 4, "abov": 4, "divis": 4, "simplifi": 4, "dimensionless": 4, "analys": 4, "nonzero": 4, "analysi": 4, "long": 4, "avoid": 4, "alias": 4, "good": 4, "choic": 4, "kept": 4, "mysign": 4, "5000e3": 4, "nd": 4, "stack": 4, "row": 4, "number_of_sign": 4, "decai": 4, "f1": 4, "chirp_dur": 4, "step": 4, "odd": 4, "suppress": 4, "pad": 4, "miss": 4, "greater": 4, "max": 4, "wavelet": 4, "better": 4, "round": 4, "han": 4, "sinusoid": 4, "fmax": 4, "choos": 4, "4fc": 4, "last": 4, "sweep": 4}, "objects": {"": [[4, 0, 0, "-", "waveguicsx"]], "waveguicsx": [[4, 0, 0, "-", "waveguide"]], "waveguicsx.waveguide": [[4, 1, 1, "", "Signal"], [4, 1, 1, "", "Waveguide"]], "waveguicsx.waveguide.Signal": [[4, 2, 1, "", "chirp"], [4, 2, 1, "", "fft"], [4, 2, 1, "", "ifft"], [4, 2, 1, "", "plot"], [4, 2, 1, "", "plot_spectrum"], [4, 2, 1, "", "ricker"], [4, 2, 1, "", "toneburst"]], "waveguicsx.waveguide.Waveguide": [[4, 2, 1, "", "compute_eigenforces"], [4, 2, 1, "", "compute_energy_velocity"], [4, 2, 1, "", "compute_group_velocity"], [4, 2, 1, "", "compute_opposite_going"], [4, 2, 1, "", "compute_pml_ratio"], [4, 2, 1, "", "compute_poynting_normalization"], [4, 2, 1, "", "compute_response"], [4, 2, 1, "", "compute_response_coefficient"], [4, 2, 1, "", "compute_traveling_direction"], [4, 2, 1, "", "plot"], [4, 2, 1, "", "plot_attenuation"], [4, 2, 1, "", "plot_coefficient"], [4, 2, 1, "", "plot_energy_velocity"], [4, 2, 1, "", "plot_excitability"], [4, 2, 1, "", "plot_group_velocity"], [4, 2, 1, "", "plot_phase_velocity"], [4, 2, 1, "", "plot_spectrum"], [4, 2, 1, "", "set_parameters"], [4, 2, 1, "", "set_plot_scaler"], [4, 2, 1, "", "solve"]]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:method"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "method", "Python method"]}, "titleterms": {"document": [0, 2], "waveguicsx": [1, 2, 4], "version": 1, "1": [1, 2, 3], "content": 1, "present": 2, "0": [2, 3], "introduct": 2, "basic": 2, "exampl": 2, "2": [2, 3], "prerequisit": 2, "3": [2, 3], "instal": 2, "clone": 2, "public": 2, "repositori": 2, "gener": 2, "docker": 2, "imag": 2, "run": 2, "contain": 2, "us": [2, 3], "4": [2, 3], "5": [2, 3], "tutori": [2, 3], "6": [2, 3], "author": 2, "contributor": 2, "7": [2, 3], "how": 2, "cite": 2, "8": 2, "licens": 2, "3d": [], "elast": 3, "bar": 3, "squar": 3, "cross": 3, "section": 3, "parallel": 3, "buri": 3, "pml": 3, "extern": 3, "medium": 3, "gmsh": 3, "excit": 3, "circular": 3, "time": 3, "respons": 3, "plate": 3, "its": 3, "first": 3, "zgv": 3, "reson": 3, "dispers": 3, "curv": 3, "rail": 3, "waveguid": 4, "attribut": 4, "method": 4, "paramet": 4, "return": 4, "three": 3, "dimension": 3, "two": 3}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 8, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.todo": 2, "sphinx.ext.viewcode": 1, "nbsphinx": 4, "sphinx": 57}, "alltitles": {"Documentation": [[0, "documentation"]], "Waveguicsx - Version 1.1": [[1, "waveguicsx-version-version"]], "Contents:": [[1, null]], "Presentation": [[2, "presentation"]], "0. Introduction": [[2, "introduction"]], "1. Basic examples": [[2, "basic-examples"]], "2. Prerequisites": [[2, "prerequisites"]], "3. Installation": [[2, "installation"]], "3.1 Clone the Waveguicsx public repository": [[2, "clone-the-waveguicsx-public-repository"]], "3.2 Generate the docker image and run the container using :": [[2, "generate-the-docker-image-and-run-the-container-using"]], "4. Documentation": [[2, "documentation"]], "5. Tutorials": [[2, "tutorials"]], "6. Authors and contributors": [[2, "authors-and-contributors"]], "7. How to cite": [[2, "how-to-cite"]], "8. License": [[2, "license"]], "Tutorials": [[3, "tutorials"]], "0. Three-dimensional elastic bar of square cross-section": [[3, "three-dimensional-elastic-bar-of-square-cross-section"]], "1. Three-dimensional elastic bar of square cross-section with parallelization": [[3, "three-dimensional-elastic-bar-of-square-cross-section-with-parallelization"]], "2. Three-dimensional elastic bar of square cross-section buried into a PML external medium": [[3, "three-dimensional-elastic-bar-of-square-cross-section-buried-into-a-pml-external-medium"]], "3. Three-dimensional elastic bar of square cross-section buried into a PML external medium using gmsh": [[3, "three-dimensional-elastic-bar-of-square-cross-section-buried-into-a-pml-external-medium-using-gmsh"]], "4. Excitation of a three-dimensional elastic bar of circular cross-section": [[3, "excitation-of-a-three-dimensional-elastic-bar-of-circular-cross-section"]], "5. Excitation of a three-dimensional elastic bar of circular cross-section with parallelization": [[3, "excitation-of-a-three-dimensional-elastic-bar-of-circular-cross-section-with-parallelization"]], "6. Time response of a two-dimensional plate excited near its first ZGV resonance": [[3, "time-response-of-a-two-dimensional-plate-excited-near-its-first-zgv-resonance"]], "7. Dispersion curves of a rail": [[3, "dispersion-curves-of-a-rail"]], "waveguicsx.waveguide": [[4, "module-waveguicsx"]], "Attributes": [[4, "attributes"], [4, "id32"]], "Methods": [[4, "methods"], [4, "id33"]], "Parameters": [[4, "parameters"], [4, "id19"], [4, "id22"], [4, "id23"], [4, "id24"], [4, "id28"]], "Returns": [[4, "returns"], [4, "id27"], [4, "id31"]]}, "indexentries": {"signal (class in waveguicsx.waveguide)": [[4, "waveguicsx.waveguide.Signal"]], "waveguide (class in waveguicsx.waveguide)": [[4, "waveguicsx.waveguide.Waveguide"]], "chirp() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.chirp"]], "compute_eigenforces() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_eigenforces"]], "compute_energy_velocity() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_energy_velocity"]], "compute_group_velocity() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_group_velocity"]], "compute_opposite_going() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_opposite_going"]], "compute_pml_ratio() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_pml_ratio"]], "compute_poynting_normalization() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_poynting_normalization"]], "compute_response() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_response"]], "compute_response_coefficient() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_response_coefficient"]], "compute_traveling_direction() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.compute_traveling_direction"]], "fft() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.fft"]], "ifft() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.ifft"]], "module": [[4, "module-waveguicsx"], [4, "module-waveguicsx.waveguide"]], "plot() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.plot"]], "plot() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot"]], "plot_attenuation() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_attenuation"]], "plot_coefficient() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_coefficient"]], "plot_energy_velocity() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_energy_velocity"]], "plot_excitability() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_excitability"]], "plot_group_velocity() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_group_velocity"]], "plot_phase_velocity() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_phase_velocity"]], "plot_spectrum() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.plot_spectrum"]], "plot_spectrum() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.plot_spectrum"]], "ricker() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.ricker"]], "set_parameters() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.set_parameters"]], "set_plot_scaler() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.set_plot_scaler"]], "solve() (waveguicsx.waveguide.waveguide method)": [[4, "waveguicsx.waveguide.Waveguide.solve"]], "toneburst() (waveguicsx.waveguide.signal method)": [[4, "waveguicsx.waveguide.Signal.toneburst"]], "waveguicsx": [[4, "module-waveguicsx"]], "waveguicsx.waveguide": [[4, "module-waveguicsx.waveguide"]]}}) \ No newline at end of file diff --git a/docs/tutorials.html b/docs/tutorials.html new file mode 100644 index 0000000..7051fee --- /dev/null +++ b/docs/tutorials.html @@ -0,0 +1,1827 @@ + + + + + + + Tutorials — waveguicsx 1.1 documentation + + + + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +
    +

    Tutorials

    +
    +

    0. Three-dimensional elastic bar of square cross-section

    +

    3D (visco-)elastic waveguide example. +The cross-section is a 2D square with free boundary conditions on its boundaries. +Material: viscoelastic steel. +The SAFE eigenproblem is solved with the varying parameter as the wavenumber (eigenvalues are then frequencies) or as the frequency (eigenvalues are then wavenumbers). +Viscoelastic loss is included by introducing imaginary parts (negative) to wave celerities. +Results are compared with Euler-Bernoulli analytical solutions for the lowest wavenumber or frequency parameter.

    +
    # This file is a tutorial for waveguicsx (*), whose inputs are
    +# the matrices K0, K1, K2, M and the excitation vector F.
    +# In the tutorial, K0, K1, K2 and M are finite element matrices generated by FEnicSX (**).
    +#  (*) waveguicsx is a python library for solving complex waveguide problems
    +#      Copyright (C) 2023-2024  Fabien Treyssede
    +#      waveguicsx is free software distributed under the GNU General Public License
    +#      (https://github.com/treyssede/waveguicsx)
    +# (**) FEniCSx is an open-source computing platform for solving partial differential equations
    +#      distributed under the GNU Lesser General Public License (https://fenicsproject.org/)
    +
    +##################################
    +# 3D (visco-)elastic waveguide example\
    +# The cross-section is a 2D square with free boundary conditions on its boundaries\
    +# Material: viscoelastic steel\
    +# The waveguide FE formulation (SAFE) leads to the following eigenvalue problem:\
    +# $(\textbf{K}_1-\omega^2\textbf{M}+\text{i}k(\textbf{K}_2+\textbf{K}_2^\text{T})+k^2\textbf{K}_3)\textbf{U}=\textbf{0}$\
    +# This eigenproblem is solved with the varying parameter as the wavenumber (eigenvalues are then frequencies) or as the frequency (eigenvalues are then wavenumbers)\
    +# Viscoelastic loss is included by introducing imaginary parts (negative) to wave celerities\
    +# Results are compared with Euler-Bernoulli analytical solutions for the lowest wavenumber or frequency parameter
    +
    +import dolfinx
    +import ufl
    +from mpi4py import MPI
    +from petsc4py import PETSc
    +from slepc4py import SLEPc
    +import numpy as np
    +import matplotlib.pyplot as plt
    +import pyvista
    +
    +from waveguicsx.waveguide import Waveguide
    +#For proper use with a jupyter notebook, uncomment the following line:
    +#pyvista.set_jupyter_backend("static"); pyvista.start_xvfb() #try: "none", "static", "pythreejs", "ipyvtklink"...
    +
    +##################################
    +# Input parameters
    +a = 2.7e-3 #square half-length (m)
    +N = 10 #number of finite elements along one half-side
    +rho, cs, cl = 7932, 3260, 5960 #core density (kg/m3), shear and longitudinal wave celerities (m/s)
    +kappas, kappal = 0.008, 0.003 #core shear and longitudinal bulk wave attenuations (Np/wavelength)
    +omega = 2*np.pi*np.linspace(500e3/1000, 500e3, num=50) #angular frequency range (rad/s)
    +wavenumber = omega/cs #wavenumber range (1/m, eigenvalues are frequency)
    +nev = 20 #number of eigenvalues
    +
    +##################################
    +# Re-scaling
    +L0 = a #characteristic length
    +T0 = a/cs #characteristic time
    +M0 = rho*a**3#characteristic mass
    +a = a/L0
    +rho, cs, cl = rho/M0*L0**3, cs/L0*T0, cl/L0*T0
    +omega = omega*T0
    +wavenumber = wavenumber*L0
    +cs, cl = cs/(1+1j*kappas/2/np.pi), cl/(1+1j*kappal/2/np.pi) #complex celerities (core)
    +
    +##################################
    +# Create mesh and finite elements (six-node triangles with three dofs per node for the three components of displacement)
    +mesh = dolfinx.mesh.create_rectangle(MPI.COMM_WORLD, [np.array([-a, -a]), np.array([a, a])], 
    +                               [2*N, 2*N], dolfinx.mesh.CellType.triangle)
    +element = ufl.VectorElement("CG", "triangle", 2, 3) #Lagrange element, triangle, quadratic "P2", 3D vector
    +V = dolfinx.fem.FunctionSpace(mesh, element)
    +
    +##################################
    +# Create Material properties (isotropic)
    +def isotropic_law(rho, cs, cl):
    +    E, nu = rho*cs**2*(3*cl**2-4*cs**2)/(cl**2-cs**2), 0.5*(cl**2-2*cs**2)/(cl**2-cs**2)
    +    C11 = C22 = C33 = E/(1+nu)/(1-2*nu)*(1-nu)
    +    C12 = C13 = C23 = E/(1+nu)/(1-2*nu)*nu
    +    C44 = C55 = C66 = E/(1+nu)/2
    +    return ((C11,C12,C13,0,0,0), 
    +            (C12,C22,C23,0,0,0), 
    +            (C13,C23,C33,0,0,0), 
    +            (0,0,0,C44,0,0), 
    +            (0,0,0,0,C55,0), 
    +            (0,0,0,0,0,C66))
    +C = isotropic_law(rho, cs, cl)
    +C = dolfinx.fem.Constant(mesh, PETSc.ScalarType(C))
    +
    +##################################
    +# Create free boundary conditions (or uncomment lines below for Dirichlet)
    +bcs = []
    +# Dirichlet test case:
    +#fdim = mesh.topology.dim - 1
    +#boundary_facets = dolfinx.mesh.locate_entities_boundary(mesh, dim=fdim, marker=lambda x: np.logical_or(np.isclose(x[0], -a),
    +#                                                                                                       np.isclose(x[0], +a)+
    +#                                                                                                       np.isclose(x[1], -a)+
    +#                                                                                                       np.isclose(x[1], +a)))
    +#boundary_dofs = dolfinx.fem.locate_dofs_topological(V=V, entity_dim=fdim, entities=boundary_facets)
    +#bcs = [dolfinx.fem.dirichletbc(value=np.zeros(3, dtype=PETSc.ScalarType), dofs=boundary_dofs, V=V)]
    +
    +##################################
    +# Define variational problem (SAFE method)
    +u = ufl.TrialFunction(V)
    +v = ufl.TestFunction(V)
    +Lxy = lambda u: ufl.as_vector([u[0].dx(0), u[1].dx(1), 0, u[0].dx(1)+u[1].dx(0), u[2].dx(0), u[2].dx(1)])
    +Lz  = lambda u: ufl.as_vector([0, 0, u[2], 0, u[0], u[1]])
    +k0 = ufl.inner(C*Lxy(u), Lxy(v)) * ufl.dx
    +k0_form = dolfinx.fem.form(k0)
    +k1 = ufl.inner(C*Lz(u), Lxy(v)) * ufl.dx
    +k1_form = dolfinx.fem.form(k1)
    +k2 = ufl.inner(C*Lz(u), Lz(v)) * ufl.dx
    +k2_form = dolfinx.fem.form(k2)
    +m = rho*ufl.inner(u, v) * ufl.dx
    +mass_form = dolfinx.fem.form(m)
    +
    +##################################
    +# Build PETSc matrices
    +M = dolfinx.fem.petsc.assemble_matrix(mass_form, bcs=bcs, diagonal=0.0)
    +M.assemble()
    +K0 = dolfinx.fem.petsc.assemble_matrix(k0_form, bcs=bcs)
    +K0.assemble()
    +K1 = dolfinx.fem.petsc.assemble_matrix(k1_form, bcs=bcs, diagonal=0.0)
    +K1.assemble()
    +K2 = dolfinx.fem.petsc.assemble_matrix(k2_form, bcs=bcs, diagonal=0.0)
    +K2.assemble()
    +
    +##################################
    +# Solve the eigenproblem with SLEPc\
    +# The parameter is k, the eigenvalue is omega**2
    +wg = Waveguide(MPI.COMM_WORLD, M, K0, K1, K2)
    +wg.set_parameters(wavenumber=wavenumber)
    +wg.solve(nev) #access to components with: wg.eigenvalues[ik][imode], wg.eigenvectors[ik][idof,imode]
    +wg.plot()
    +plt.show()
    +
    +##################################
    +# Comparison with Euler-Bernoulli analytical solutions
    +k = wg.wavenumber[0]
    +print(f'Computed eigenvalues for the first wavenumber (k={k}):\n {np.around(wg.eigenvalues[0],decimals=6)}')
    +E, mu, I, Ip, S = rho*cs**2*(3*cl**2-4*cs**2)/(cl**2-cs**2), rho*cs**2, (2*a)**4/12, 0.1406*(2*a)**4, (2*a)**2 #0.1406 is for square section
    +print(f'Euler-Bernoulli beam solution (only accurate for low frequency):\n \
    +        Bending wave: {np.around(np.sqrt(E*I/rho/S*k**4),decimals=6)}\n \
    +        Torsional wave: {np.around(np.sqrt(mu*Ip/rho/(2*I)*k**2),decimals=6)}\n \
    +        Longitudinal wave: {np.around(np.sqrt(E/rho*k**2),decimals=6)}')
    +
    +##################################
    +# Solve the eigenproblem with SLEPc\
    +# The parameter is omega, the eigenvalue is k
    +wg = Waveguide(MPI.COMM_WORLD, M, K0, K1, K2)
    +wg.set_parameters(omega=omega)
    +wg.solve(nev) #access to components with: wg.eigenvalues[ik][imode], wg.eigenvectors[ik][idof,imode]
    +wg.plot()
    +plt.show() #blocking
    +
    +##################################
    +# Comparison with Euler-Bernoulli analytical solutions
    +w = wg.omega[0]
    +print(f'Computed eigenvalues for the first frequency (omega={w}):\n {np.around(wg.eigenvalues[0],decimals=6)}')
    +print(f'Euler-Bernoulli beam solution (only accurate for low frequency):\n \
    +        Bending wave: {np.around(np.sqrt(np.sqrt(rho*S/E/I*w**2)),decimals=6)}\n \
    +        Torsional wave: {np.around(np.sqrt(rho*2*I/mu/Ip*w**2),decimals=6)}\n \
    +        Longitudinal wave: {np.around(np.sqrt(rho/E*w**2),decimals=6)}')
    +
    +##################################
    +# Mesh visualization
    +grid = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(mesh, mesh.topology.dim))
    +plotter = pyvista.Plotter()
    +plotter.add_mesh(grid, show_edges=True)
    +plotter.view_xy()
    +plotter.show()
    +
    +##################################
    +# Mode shape visualization
    +ik, imode = 0, 5 #parameter index, mode index to visualize
    +vec = wg.eigenvectors[ik].getColumnVector(imode)
    +u_grid = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(V))
    +u_grid["u"] = np.array(vec).real.reshape(int(np.array(vec).size/V.element.value_shape), int(V.element.value_shape)) #V.element.value_shape is equal to 3
    +u_plotter = pyvista.Plotter()
    +u_plotter.add_mesh(grid, style="wireframe", color="k") #FE mesh
    +u_plotter.add_mesh(u_grid.warp_by_vector("u", factor=2.0), opacity=0.8, show_scalar_bar=True, show_edges=False) #do not show edges of higher order elements with pyvista
    +u_plotter.show_axes()
    +u_plotter.show()
    +
    +
    +""
    +
    +
    +
    +
    +
    +

    1. Three-dimensional elastic bar of square cross-section with parallelization

    +

    3D (visco-)elastic waveguide example. +The cross-section is a 2D square with free boundary conditions on its 1D boundaries. +Material: viscoelastic steel. +Viscoelastic loss is included by introducing imaginary parts (negative) to wave celerities. +In this example:

    +
      +
    • the parameter loop (here, the frequency loop) is distributed on all processes

    • +
    • FE mesh and matrices are built on each local process

    • +
    +
    # This file is a tutorial for waveguicsx (*), whose inputs are
    +# the matrices K0, K1, K2, M and the excitation vector F.
    +# In the tutorial, K0, K1, K2 and M are finite element matrices generated by FEnicSX (**).
    +#  (*) waveguicsx is a python library for solving complex waveguide problems
    +#      Copyright (C) 2023-2024  Fabien Treyssede
    +#      waveguicsx is free software distributed under the GNU General Public License
    +#      (https://github.com/treyssede/waveguicsx)
    +# (**) FEniCSx is an open-source computing platform for solving partial differential equations
    +#      distributed under the GNU Lesser General Public License (https://fenicsproject.org/)
    +
    +##################################
    +# 3D (visco-)elastic waveguide example\
    +# The cross-section is a 2D square with free boundary conditions on its 1D boundaries\
    +# Material: viscoelastic steel\
    +# The waveguide FE formulation (SAFE) leads to the following eigenvalue problem:\
    +# $(\textbf{K}_1-\omega^2\textbf{M}+\text{i}k(\textbf{K}_2+\textbf{K}_2^\text{T})+k^2\textbf{K}_3)\textbf{U}=\textbf{0}$\
    +# Viscoelastic loss is included by introducing imaginary parts (negative) to wave celerities\
    +# In this example:
    +# - the parameter loop (here, the frequency loop) is distributed on all processes
    +# - FE mesh and matrices are built on each local process
    +# Reminder for an execution in parallel mode (e.g. 4 processes):
    +#  mpiexec -n 4 python3 Elastic_Waveguide_SquareBar3D_ParallelizedLoop.py
    +
    +import dolfinx
    +import ufl
    +from mpi4py import MPI
    +from petsc4py import PETSc
    +from slepc4py import SLEPc
    +import numpy as np
    +import matplotlib.pyplot as plt
    +#import pyvista
    +
    +from waveguicsx.waveguide import Waveguide
    +#For proper use with a jupyter notebook, uncomment the following line:
    +#pyvista.set_jupyter_backend("static"); pyvista.start_xvfb() #try: "none", "static", "pythreejs", "ipyvtklink"...
    +
    +##################################
    +# Input parameters
    +a = 2.7e-3 #square half-length (m)
    +N = 10 #number of finite elements along one half-side
    +rho, cs, cl = 7932, 3260, 5960 #core density (kg/m3), shear and longitudinal wave celerities (m/s)
    +kappas, kappal = 0.008, 0.003 #core shear and longitudinal bulk wave attenuations (Np/wavelength)
    +omega = 2*np.pi*np.linspace(500e3/1000, 500e3, num=50) #angular frequency range (rad/s)
    +nev = 20 #number of eigenvalues
    +
    +##################################
    +# Re-scaling
    +L0 = a #characteristic length
    +T0 = a/cs #characteristic time
    +M0 = rho*a**3#characteristic mass
    +a = a/L0
    +rho, cs, cl = rho/M0*L0**3, cs/L0*T0, cl/L0*T0
    +omega = omega*T0
    +cs, cl = cs/(1+1j*kappas/2/np.pi), cl/(1+1j*kappal/2/np.pi) #complex celerities (core)
    +
    +##################################
    +# Create mesh and finite elements (six-node triangles with three dofs per node for the three components of displacement)
    +mesh = dolfinx.mesh.create_rectangle(MPI.COMM_SELF, [np.array([-a, -a]), np.array([a, a])], 
    +                               [2*N, 2*N], dolfinx.mesh.CellType.triangle) #MPI.COMM_SELF = FE mesh is built on each local process
    +element = ufl.VectorElement("CG", "triangle", 2, 3) #Lagrange element, triangle, quadratic "P2", 3D vector
    +V = dolfinx.fem.FunctionSpace(mesh, element)
    +
    +##################################
    +# Create Material properties (isotropic)
    +def isotropic_law(rho, cs, cl):
    +    E, nu = rho*cs**2*(3*cl**2-4*cs**2)/(cl**2-cs**2), 0.5*(cl**2-2*cs**2)/(cl**2-cs**2)
    +    C11 = C22 = C33 = E/(1+nu)/(1-2*nu)*(1-nu)
    +    C12 = C13 = C23 = E/(1+nu)/(1-2*nu)*nu
    +    C44 = C55 = C66 = E/(1+nu)/2
    +    return ((C11,C12,C13,0,0,0), 
    +            (C12,C22,C23,0,0,0), 
    +            (C13,C23,C33,0,0,0), 
    +            (0,0,0,C44,0,0), 
    +            (0,0,0,0,C55,0), 
    +            (0,0,0,0,0,C66))
    +C = isotropic_law(rho, cs, cl)
    +C = dolfinx.fem.Constant(mesh, PETSc.ScalarType(C))
    +
    +##################################
    +# Create free boundary conditions
    +bcs = []
    +
    +##################################
    +# Define variational problem (SAFE method)
    +u = ufl.TrialFunction(V)
    +v = ufl.TestFunction(V)
    +Lxy = lambda u: ufl.as_vector([u[0].dx(0), u[1].dx(1), 0, u[0].dx(1)+u[1].dx(0), u[2].dx(0), u[2].dx(1)])
    +Lz  = lambda u: ufl.as_vector([0, 0, u[2], 0, u[0], u[1]])
    +k0 = ufl.inner(C*Lxy(u), Lxy(v)) * ufl.dx
    +k0_form = dolfinx.fem.form(k0)
    +k1 = ufl.inner(C*Lz(u), Lxy(v)) * ufl.dx
    +k1_form = dolfinx.fem.form(k1)
    +k2 = ufl.inner(C*Lz(u), Lz(v)) * ufl.dx
    +k2_form = dolfinx.fem.form(k2)
    +m = rho*ufl.inner(u, v) * ufl.dx
    +mass_form = dolfinx.fem.form(m)
    +
    +##################################
    +# Build PETSc matrices
    +M = dolfinx.fem.petsc.assemble_matrix(mass_form, bcs=bcs, diagonal=0.0)
    +M.assemble()
    +K0 = dolfinx.fem.petsc.assemble_matrix(k0_form, bcs=bcs)
    +K0.assemble()
    +K1 = dolfinx.fem.petsc.assemble_matrix(k1_form, bcs=bcs, diagonal=0.0)
    +K1.assemble()
    +K2 = dolfinx.fem.petsc.assemble_matrix(k2_form, bcs=bcs, diagonal=0.0)
    +K2.assemble()
    +
    +##################################
    +# Solve the eigenproblem with SLEPc\
    +# The parameter is k, the eigenvalue is omega**2
    +# The parameter loop is parallelized
    +# Parallelization
    +comm = MPI.COMM_WORLD #use all processes for the loop
    +size = comm.Get_size()  #number of processors
    +rank = comm.Get_rank()  #returns the rank of the process that called it within comm_world
    +# Split the parameter range and scatter to all
    +if rank == 0: #define on rank 0 only
    +    param_split = np.array_split(omega, size) #split param in blocks of length size roughly
    +else:
    +    param_split = None
    +param_local = comm.scatter(param_split, root=0) #scatter 1 block per process
    +# Solve
    +wg = Waveguide(MPI.COMM_SELF, M, K0, K1, K2) #MPI.COMM_SELF = SLEPc will used FE matrices on each local process
    +wg.set_parameters(omega=param_local)
    +wg.solve(nev)
    +# Some post-processing
    +wg.compute_energy_velocity()
    +wg.compute_traveling_direction()
    +# Gather
    +wg.omega = comm.reduce([wg.omega], op=MPI.SUM, root=0) #reduce works for lists: brackets are necessary (wg.omega is not a list but a numpy array)
    +wg.eigenvalues = comm.reduce(wg.eigenvalues, op=MPI.SUM, root=0)
    +wg.energy_velocity = comm.reduce(wg.energy_velocity, op=MPI.SUM, root=0)
    +wg.traveling_direction = comm.reduce(wg.traveling_direction, op=MPI.SUM, root=0)
    +#wg.eigenvectors = comm.reduce(wg.eigenvectors, op=MPI.SUM, root=0) #don't do this line: reduce cannot pickle 'petsc4py.PETSc.Vec' objects (keep the mode shapes distributed on each processor rather than gather them)
    +# Plot results
    +if rank == 0:
    +    wg.omega = np.concatenate(wg.omega) #wg.omega is transformed to a numpy array for a proper use of wg.plot()
    +    wg.plot()
    +    wg.plot_energy_velocity(direction=+1)
    +    #plt.savefig("Elastic_Waveguide_Bar3D_ParallelizedLoop.svg")
    +    plt.show()
    +
    +
    +
    +
    +
    +

    2. Three-dimensional elastic bar of square cross-section buried into a PML external medium

    +

    3D (visco-)elastic waveguide example. +The cross-section is a 2D square buried into a PML external elastic medium. +Material: viscoelastic steel into cement grout. +Viscoelastic loss is included by introducing imaginary parts (negative) to wave celerities. +The PML has a parabolic profile. +Results are to be compared with Fig. 8 of paper: Treyssede, Journal of Computational Physics 314 (2016), 341–354.

    +
    # This file is a tutorial for waveguicsx (*), whose inputs are
    +# the matrices K0, K1, K2, M and the excitation vector F.
    +# In the tutorial, K0, K1, K2 and M are finite element matrices generated by FEnicSX (**).
    +#  (*) waveguicsx is a python library for solving complex waveguide problems
    +#      Copyright (C) 2023-2024  Fabien Treyssede
    +#      waveguicsx is free software distributed under the GNU General Public License
    +#      (https://github.com/treyssede/waveguicsx)
    +# (**) FEniCSx is an open-source computing platform for solving partial differential equations
    +#      distributed under the GNU Lesser General Public License (https://fenicsproject.org/)
    +
    +##################################
    +# 3D (visco-)elastic waveguide example\
    +# The cross-section is a 2D square buried into a PML external elastic medium\
    +# Material: viscoelastic steel into cement grout\
    +# The waveguide FE formulation (SAFE) leads to the following eigenvalue problem:\
    +# $(\textbf{K}_1-\omega^2\textbf{M}+\text{i}k(\textbf{K}_2+\textbf{K}_2^\text{T})+k^2\textbf{K}_3)\textbf{U}=\textbf{0}$\
    +# Viscoelastic loss is included by introducing imaginary parts (negative) to wave celerities\
    +# The PML has a parabolic profile
    +# Results are to be compared with Fig. 8 of paper: Treyssede, Journal of Computational Physics 314 (2016), 341–354
    +
    +import dolfinx
    +import ufl
    +from mpi4py import MPI
    +from petsc4py import PETSc
    +from slepc4py import SLEPc
    +import numpy as np
    +import matplotlib.pyplot as plt
    +import pyvista
    +
    +from waveguicsx.waveguide import Waveguide
    +#For proper use with a jupyter notebook, uncomment the following line:
    +#pyvista.set_jupyter_backend("static"); pyvista.start_xvfb() #try: "none", "static", "pythreejs", "ipyvtklink"...
    +
    +##################################
    +# Input parameters
    +a = 2.7e-3 #core half-length (m)
    +b = 1.5*a #half-length including PML (m)
    +N = 6 #24 #number of finite elements along one half-side
    +if not (a*N/b).is_integer():
    +    raise NotImplementedError("The ratio a/b*N must be an integer, please adjust N")
    +rho_core, cs_core, cl_core = 7932, 3260, 5960 #core density (kg/m3), shear and longitudinal wave celerities (m/s)
    +kappas_core, kappal_core = 0.008, 0.003 #core shear and longitudinal bulk wave attenuations (Np/wavelength)
    +rho_ext, cs_ext, cl_ext = 1600, 1700, 2810 #for the external medium
    +kappas_ext, kappal_ext = 0.100, 0.043 #for the external medium
    +alpha = 2+4j #average value of the absorbing function inside the PML
    +omega = 2*np.pi*np.linspace(0, 26e6/(a*1e3), num=200) #angular frequencies (rad/s)
    +nev = 10 #number of eigenvalues
    +target = lambda omega: omega/cl_core.real #target set at the longitudinal wavenumber of core to find L(0,n) modes
    +
    +##################################
    +# Re-scaling
    +L0 = a #characteristic length
    +T0 = a/cs_core #characteristic time
    +M0 = rho_core*a**3#characteristic mass
    +a, b = a/L0, b/L0
    +rho_core, cs_core, cl_core = rho_core/M0*L0**3, cs_core/L0*T0, cl_core/L0*T0
    +rho_ext, cs_ext, cl_ext = rho_ext/M0*L0**3, cs_ext/L0*T0, cl_ext/L0*T0
    +omega = omega*T0
    +cs_core, cl_core = cs_core/(1+1j*kappas_core/2/np.pi), cl_core/(1+1j*kappal_core/2/np.pi) #complex celerities (core)
    +cs_ext, cl_ext = cs_ext/(1+1j*kappas_ext/2/np.pi), cl_ext/(1+1j*kappal_ext/2/np.pi) #complex celerities (exterior)
    +
    +##################################
    +# Create mesh and finite elements (P2 quadrilaterals or order 8 GLL, with three dofs per node for the three components of displacement)
    +mesh = dolfinx.mesh.create_rectangle(MPI.COMM_WORLD, [np.array([-b, -b]), np.array([b, b])], 
    +                               [2*N, 2*N], dolfinx.mesh.CellType.quadrilateral)
    +#element = ufl.VectorElement("CG", "quadrilateral", 2, 3) #Lagrange element, quadrilateral, quadratic "P2", 3D vector
    +import basix
    +element = basix.create_element(basix.ElementFamily.P, basix.CellType.quadrilateral, 8,
    +                               basix.LagrangeVariant.gll_warped)
    +element = basix.ufl_wrapper.BasixElement(element)
    +element = basix.ufl_wrapper.VectorElement(element, 3)
    +V = dolfinx.fem.FunctionSpace(mesh, element)
    +
    +##################################
    +# Create Material properties, discontinuous between core and exterior
    +def isotropic_law(rho, cs, cl):
    +    E, nu = rho*cs**2*(3*cl**2-4*cs**2)/(cl**2-cs**2), 0.5*(cl**2-2*cs**2)/(cl**2-cs**2)
    +    C11 = C22 = C33 = E/(1+nu)/(1-2*nu)*(1-nu)
    +    C12 = C13 = C23 = E/(1+nu)/(1-2*nu)*nu
    +    C44 = C55 = C66 = E/(1+nu)/2
    +    C = np.array([[C11,C12,C13,0,0,0], 
    +                     [C12,C22,C23,0,0,0], 
    +                     [C13,C23,C33,0,0,0], 
    +                     [0,0,0,C44,0,0], 
    +                     [0,0,0,0,C55,0], 
    +                     [0,0,0,0,0,C66]])
    +    C = C[np.triu_indices(6)] #the upper-triangle part of C (21 elements only)...
    +    C = np.append(C,np.zeros(15)).reshape(36,1) #... stored as a column vector completed with zeros up to 36 elements (error otherwise)
    +    return C
    +C_core = isotropic_law(rho_core, cs_core, cl_core)
    +C_ext = isotropic_law(rho_ext, cs_ext, cl_ext)
    +def eval_C(x):
    +    values = C_core * np.logical_and(abs(x[0])<=a, abs(x[1])<=a) \
    +           + C_ext * np.logical_or(abs(x[0])>=a, abs(x[1])>=a)
    +    return values
    +Q = dolfinx.fem.FunctionSpace(mesh, ufl.TensorElement("DG", "quadrilateral", 0, (6,6), symmetry=True)) #symmetry enables to store 21 elements instead of 36
    +C = dolfinx.fem.Function(Q)
    +C.interpolate(eval_C) #C.vector.getSize()) should be equal to number of cells x 21
    +# Same approach for density
    +def eval_rho(x):
    +    values = rho_core * np.logical_and(abs(x[0])<=a, abs(x[1])<=a) \
    +           + rho_ext * np.logical_or(abs(x[0])>=a, abs(x[1])>=a)
    +    return values
    +Q = dolfinx.fem.FunctionSpace(mesh, ("DG", 0))
    +rho = dolfinx.fem.Function(Q)
    +rho.interpolate(eval_rho)
    +# Vizualization
    +plotter = pyvista.Plotter(window_size=[600, 400])
    +grid = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(mesh, mesh.topology.dim))
    +grid.cell_data["Cij"] = C.x.array[0::21].real #index from 0 to 20, 0 being for C11...
    +#grid.cell_data["Cij"] = rho.x.array.real #for checking density
    +grid.set_active_scalars("Cij")
    +plotter.add_mesh(grid, show_edges=True, show_scalar_bar=True)
    +plotter.add_text('Re(Cij)', 'upper_edge', color='black', font_size=8)
    +plotter.view_xy()
    +plotter.show()
    +
    +##################################
    +# Create Cartesian PML functions, continuous
    +def eval_gamma(x): #pml profile: continuous and parabolic
    +    values = [1+3*(alpha-1)*((abs(x[0])-a)/(b-a))**2*(abs(x[0])>=a), #gammax
    +              1+3*(alpha-1)*((abs(x[1])-a)/(b-a))**2*(abs(x[1])>=a)] #gammay
    +    return values
    +Q = dolfinx.fem.FunctionSpace(mesh, ufl.VectorElement("CG", "quadrilateral", 2, 2))
    +gamma = dolfinx.fem.Function(Q)
    +gamma.interpolate(eval_gamma)
    +# Vizualization
    +plotter = pyvista.Plotter(window_size=[800, 400], shape=(1,2))
    +gridx = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(Q))
    +gridx.point_data["gammax"] = gamma.x.array[0::2].imag
    +gridx.set_active_scalars("gammax")
    +plotter.subplot(0,0)
    +plotter.add_mesh(grid, style="wireframe", color="k") #FE mesh
    +plotter.add_mesh(gridx, opacity=0.8, show_scalar_bar=True, show_edges=False)
    +plotter.add_text('Im(gammax)', 'upper_edge', color='black', font_size=8)
    +plotter.view_xy()
    +gridy = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(Q))
    +gridy.point_data["gammay"] = gamma.x.array[1::2].imag
    +gridy.set_active_scalars("gammay")
    +plotter.subplot(0,1)
    +plotter.add_mesh(grid, style="wireframe", color="k") #FE mesh
    +plotter.add_mesh(gridy, opacity=0.8, show_scalar_bar=True, show_edges=False)
    +plotter.add_text('Im(gammay)', 'upper_edge', color='black', font_size=8)
    +plotter.view_xy()
    +plotter.show()
    +
    +##################################
    +# Create free boundary conditions (or uncomment lines below for Dirichlet)
    +# bcs = []
    +# Dirichlet test case:
    +fdim = mesh.topology.dim - 1
    +boundary_facets = dolfinx.mesh.locate_entities_boundary(mesh, dim=fdim, marker=lambda x: np.logical_or(np.isclose(x[0], -b),
    +                                                                                                       np.isclose(x[0], +b)+
    +                                                                                                       np.isclose(x[1], -b)+
    +                                                                                                       np.isclose(x[1], +b)))
    +boundary_dofs = dolfinx.fem.locate_dofs_topological(V=V, entity_dim=fdim, entities=boundary_facets)
    +bcs = [dolfinx.fem.dirichletbc(value=np.zeros(3, dtype=PETSc.ScalarType), dofs=boundary_dofs, V=V)]
    +
    +##################################
    +# Define variational problem (SAFE method)
    +u = ufl.TrialFunction(V)
    +v = ufl.TestFunction(V)
    +Lx = lambda u: ufl.as_vector([u[0].dx(0), 0, 0, u[1].dx(0), u[2].dx(0), 0])
    +Ly = lambda u: ufl.as_vector([0, u[1].dx(1), 0, u[0].dx(1), 0, u[2].dx(1)])
    +Lz  = lambda u: ufl.as_vector([0, 0, u[2], 0, u[0], u[1]])
    +gammax = gamma[0]
    +gammay = gamma[1]
    +k0 = (ufl.inner(C*(gammay/gammax*Lx(u)+Ly(u)), Lx(v)) + ufl.inner(C*(Lx(u)+gammax/gammay*Ly(u)), Ly(v))) * ufl.dx
    +k0_form = dolfinx.fem.form(k0)
    +k1 = (gammay*ufl.inner(C*Lz(u), Lx(v))+gammax*ufl.inner(C*Lz(u), Ly(v))) * ufl.dx
    +k1_form = dolfinx.fem.form(k1)
    +k2 = ufl.inner(C*Lz(u), Lz(v)) * gammax * gammay * ufl.dx
    +k2_form = dolfinx.fem.form(k2)
    +m = rho*ufl.inner(u, v) * gammax * gammay * ufl.dx
    +mass_form = dolfinx.fem.form(m)
    +
    +##################################
    +# Build PETSc matrices
    +M = dolfinx.fem.petsc.assemble_matrix(mass_form, bcs=bcs, diagonal=0.0)
    +M.assemble()
    +K0 = dolfinx.fem.petsc.assemble_matrix(k0_form, bcs=bcs)
    +K0.assemble()
    +K1 = dolfinx.fem.petsc.assemble_matrix(k1_form, bcs=bcs, diagonal=0.0)
    +K1.assemble()
    +K2 = dolfinx.fem.petsc.assemble_matrix(k2_form, bcs=bcs, diagonal=0.0)
    +K2.assemble()
    +
    +##################################
    +# Solve the eigenproblem with SLEPc\
    +# The parameter is omega, the eigenvalue is k
    +wg = Waveguide(MPI.COMM_WORLD, M, K0, K1, K2)
    +wg.set_parameters(omega=omega)
    +wg.evp.setWhichEigenpairs(SLEPc.PEP.Which.TARGET_MAGNITUDE) #here, preferred to TARGET_IMAGINARY
    +wg.solve(nev, target=target)
    +
    +##################################
    +# Plot dispersion curves\
    +# Results are to be compared with Fig. 8 of Treyssede, Journal of Computational Physics 314 (2016), 341–354
    +wg.plot_scaler["energy_velocity"] = L0/T0/1000 #units in m/ms
    +wg.plot_scaler["frequency"] = L0/T0/1000 #frequency units in MHz-mm
    +sc = wg.plot_energy_velocity()
    +sc.axes.set_xlim([0, 26])
    +sc.axes.set_ylim([0, 6])
    +sc.axes.set_xlabel('Frequency-halfwidth (MHz-mm)')
    +sc.axes.set_ylabel('Energy velocity (m/ms)')
    +wg.plot_scaler["attenuation"] = 8.686*1000 #units in dB-mm/m
    +sc = wg.plot_attenuation()
    +sc.axes.set_xlim([0, 26])
    +sc.axes.set_ylim([0, 2000])
    +sc.axes.set_xlabel('Frequency-halfwidth (MHz-mm)')
    +sc.axes.set_ylabel('Attenuation (dB-mm/m)')
    +plt.show()
    +#wg.plot_spectrum(index=0)
    +#plt.show()
    +
    +##################################
    +# Mode shape visualization
    +ik, imode = 50, 1 #parameter index, mode index to visualize
    +vec = wg.eigenvectors[ik].getColumnVector(imode)
    +u_grid = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(V))
    +u_grid["u"] = np.array(vec).real.reshape(int(np.array(vec).size/V.element.value_shape), int(V.element.value_shape)) #V.element.value_shape is equal to 3
    +u_plotter = pyvista.Plotter()
    +u_plotter.add_mesh(grid, style="wireframe", color="k") #FE mesh
    +u_plotter.add_mesh(u_grid.warp_by_vector("u", factor=2.0), opacity=0.8, show_scalar_bar=True, show_edges=False) #do not show edges of higher order elements with pyvista
    +u_plotter.show_axes()
    +u_plotter.show()
    +
    +
    +
    +
    +

    3. Three-dimensional elastic bar of square cross-section buried into a PML external medium using gmsh

    +

    3D (visco-)elastic waveguide example. +The cross-section is a 2D square buried into a PML external elastic medium. +Material: viscoelastic steel into cement grout. +Viscoelastic loss is included by introducing imaginary parts (negative) to wave celerities. +The PML has a parabolic profile. +The FE mesh is built from Gmsh. +Results are to be compared with Fig. 8 of paper: Treyssede, Journal of Computational Physics 314 (2016), 341–354.

    +
    # This file is a tutorial for waveguicsx (*), whose inputs are
    +# the matrices K0, K1, K2, M and the excitation vector F.
    +# In the tutorial, K0, K1, K2 and M are finite element matrices generated by FEnicSX (**).
    +#  (*) waveguicsx is a python library for solving complex waveguide problems
    +#      Copyright (C) 2023-2024  Fabien Treyssede
    +#      waveguicsx is free software distributed under the GNU General Public License
    +#      (https://github.com/treyssede/waveguicsx)
    +# (**) FEniCSx is an open-source computing platform for solving partial differential equations
    +#      distributed under the GNU Lesser General Public License (https://fenicsproject.org/)
    +
    +##################################
    +# 3D (visco-)elastic waveguide example\
    +# The cross-section is a 2D square buried into a PML external elastic medium\
    +# Material: viscoelastic steel into cement grout\
    +# The waveguide FE formulation (SAFE) leads to the following eigenvalue problem:\
    +# $(\textbf{K}_1-\omega^2\textbf{M}+\text{i}k(\textbf{K}_2+\textbf{K}_2^\text{T})+k^2\textbf{K}_3)\textbf{U}=\textbf{0}$\
    +# Viscoelastic loss is included by introducing imaginary parts (negative) to wave celerities\
    +# The PML has a parabolic profile\
    +# The FE mesh is built from Gmsh\
    +# Results are to be compared with Fig. 8 of paper: Treyssede, Journal of Computational Physics 314 (2016), 341–354
    +
    +import gmsh #full documentation entirely defined in the `gmsh.py' module
    +import dolfinx
    +import ufl
    +from mpi4py import MPI
    +from petsc4py import PETSc
    +from slepc4py import SLEPc
    +import numpy as np
    +import matplotlib.pyplot as plt
    +import pyvista
    +
    +from waveguicsx.waveguide import Waveguide
    +#For proper use with a jupyter notebook, uncomment the following line:
    +#pyvista.set_jupyter_backend("static"); pyvista.start_xvfb() #try: "none", "static", "pythreejs", "ipyvtklink"...
    +
    +##################################
    +# Input parameters
    +a = 2.7e-3 #core half-length (m)
    +b, le = 1.5*a, a/4 #half-length including PML (m), finite element characteristic length (m)
    +rho_core, cs_core, cl_core = 7932, 3260, 5960 #core density (kg/m3), shear and longitudinal wave celerities (m/s)
    +kappas_core, kappal_core = 0.008, 0.003 #core shear and longitudinal bulk wave attenuations (Np/wavelength)
    +rho_ext, cs_ext, cl_ext = 1600, 1700, 2810 #for the external medium
    +kappas_ext, kappal_ext = 0.100, 0.043 #for the external medium
    +alpha = 2+4j #average value of the absorbing function inside the PML
    +omega = 2*np.pi*np.linspace(0, 26e6/(a*1e3), num=400) #angular frequencies (rad/s)
    +nev = 10 #number of eigenvalues
    +target = lambda omega: omega/cl_core.real #target set at the longitudinal wavenumber of core to find L(0,n) modes
    +cell_type = 'quadrilateral' #'triangle', 'quadrilateral'
    +gll = True #False: Lagrange 2nd order element, True: 8th order element with Gauss-Lobatto-Legendre points (spectral-like)
    +
    +##################################
    +# Re-scaling
    +L0 = a #characteristic length
    +T0 = a/cs_core #characteristic time
    +M0 = rho_core*a**3#characteristic mass
    +a, b, le = a/L0, b/L0, le/L0
    +rho_core, cs_core, cl_core = rho_core/M0*L0**3, cs_core/L0*T0, cl_core/L0*T0
    +rho_ext, cs_ext, cl_ext = rho_ext/M0*L0**3, cs_ext/L0*T0, cl_ext/L0*T0
    +omega = omega*T0
    +cs_core, cl_core = cs_core/(1+1j*kappas_core/2/np.pi), cl_core/(1+1j*kappal_core/2/np.pi) #complex celerities (core)
    +cs_ext, cl_ext = cs_ext/(1+1j*kappas_ext/2/np.pi), cl_ext/(1+1j*kappal_ext/2/np.pi) #complex celerities (exterior)
    +
    +##################################
    +# Create mesh from Gmsh and finite elements (six-node triangles with three dofs per node for the three components of displacement)
    +gmsh.initialize()
    +# Core
    +gmsh.model.geo.addPoint(+a, -a, 0, le, 1)
    +gmsh.model.geo.addPoint(+a, +a, 0, le, 2)
    +gmsh.model.geo.addPoint(-a, +a, 0, le, 3)
    +gmsh.model.geo.addPoint(-a, -a, 0, le, 4)
    +gmsh.model.geo.addLine(1, 2, 1)
    +gmsh.model.geo.addLine(2, 3, 2)
    +gmsh.model.geo.addLine(3, 4, 3)
    +gmsh.model.geo.addLine(4, 1, 4)
    +gmsh.model.geo.addCurveLoop([1, 2, 3, 4], 1)
    +core = gmsh.model.geo.addPlaneSurface([1])
    +# External medium
    +gmsh.model.geo.addPoint(+b, -b, 0, le, 5)
    +gmsh.model.geo.addPoint(+b, +b, 0, le, 6)
    +gmsh.model.geo.addPoint(-b, +b, 0, le, 7)
    +gmsh.model.geo.addPoint(-b, -b, 0, le, 8)
    +gmsh.model.geo.addLine(5, 6, 5)
    +gmsh.model.geo.addLine(6, 7, 6)
    +gmsh.model.geo.addLine(7, 8, 7)
    +gmsh.model.geo.addLine(8, 5, 8)
    +gmsh.model.geo.addCurveLoop([5, 6, 7, 8], 2)
    +exterior = gmsh.model.geo.addPlaneSurface([2, 1])
    +# Physical groups
    +gmsh.model.geo.synchronize() #the CAD entities must be synchronized with the Gmsh model
    +gmsh.model.addPhysicalGroup(2, [core], tag=1) #2: for 2D (surface)
    +gmsh.model.addPhysicalGroup(2, [exterior], tag=2) #2: for 2D (surface)
    +gmsh.model.addPhysicalGroup(1, [5, 6, 7, 8], tag=21) #1: for 1D (line)
    +# Generate mesh
    +gmsh.model.mesh.generate(2) #generate a 2D mesh
    +if cell_type == 'quadrilateral':
    +    gmsh.model.mesh.recombine() #recombine into quadrilaterals
    +#gmsh.model.mesh.setOrder(2) #interpolation order for the geometry, here 2nd order
    +# From gmsh to fenicsx
    +mesh, cell_tags, facet_tags = dolfinx.io.gmshio.model_to_mesh(gmsh.model, MPI.COMM_WORLD, 0, gdim=2)
    +# # Reminder for save & read
    +# gmsh.write("Elastic_Waveguide_Bar3D_Open.msh") #save to disk
    +# mesh, cell_tags, facet_tags = dolfinx.io.gmshio.read_from_msh("Elastic_Waveguide_Bar3D_Open.msh", MPI.COMM_WORLD, rank=0, gdim=2)
    +gmsh.finalize() #called when done using the Gmsh Python API
    +# Finite element space
    +if not gll:
    +    element = ufl.VectorElement("CG", cell_type, 2, 3) #Lagrange element, triangle, quadratic "P2", 3D vector
    +else: #spectral element-like
    +    import basix
    +    element = basix.create_element(basix.ElementFamily.P, basix.CellType.quadrilateral if cell_type=='quadrilateral' else basix.CellType.triangle, 8,
    +                                   basix.LagrangeVariant.gll_warped)
    +    element = basix.ufl_wrapper.BasixElement(element)
    +    element = basix.ufl_wrapper.VectorElement(element, 3)
    +V = dolfinx.fem.FunctionSpace(mesh, element)
    +# Visualize FE mesh with pyvista
    +Vmesh = dolfinx.fem.FunctionSpace(mesh, ufl.FiniteElement("CG", cell_type, 1)) #cell of order 1 is properly handled with pyvista
    +plotter = pyvista.Plotter()
    +grid = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(Vmesh))
    +grid.cell_data["Marker"] = cell_tags.values
    +grid.set_active_scalars("Marker")
    +plotter.add_mesh(grid, show_edges=True)
    +grid_nodes = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(V)) #add higher-order nodes
    +plotter.add_mesh(grid_nodes, style='points', render_points_as_spheres=True, point_size=2)
    +plotter.view_xy()
    +plotter.show()
    +
    +##################################
    +# Create Material properties, discontinuous between core and exterior
    +def isotropic_law(rho, cs, cl):
    +    E, nu = rho*cs**2*(3*cl**2-4*cs**2)/(cl**2-cs**2), 0.5*(cl**2-2*cs**2)/(cl**2-cs**2)
    +    C11 = C22 = C33 = E/(1+nu)/(1-2*nu)*(1-nu)
    +    C12 = C13 = C23 = E/(1+nu)/(1-2*nu)*nu
    +    C44 = C55 = C66 = E/(1+nu)/2
    +    C = np.array([[C11,C12,C13,0,0,0], 
    +                     [C12,C22,C23,0,0,0], 
    +                     [C13,C23,C33,0,0,0], 
    +                     [0,0,0,C44,0,0], 
    +                     [0,0,0,0,C55,0], 
    +                     [0,0,0,0,0,C66]])
    +    C = C[np.triu_indices(6)] #the upper-triangle part of C (21 elements only)
    +    return PETSc.ScalarType(C)
    +C_core = isotropic_law(rho_core, cs_core, cl_core)
    +C_ext = isotropic_law(rho_ext, cs_ext, cl_ext)
    +Q = dolfinx.fem.FunctionSpace(mesh, ufl.TensorElement("DG", cell_type, 0, (6,6), symmetry=True)) #symmetry enables to store 21 elements instead of 36
    +C = dolfinx.fem.Function(Q)
    +cells = cell_tags.find(1) #core (tag=1)
    +C.x.array[[range(21*c,21*c+21) for c in cells]] = np.tile(C_core, (len(cells),1))
    +cells = cell_tags.find(2) #exterior (tag=2)
    +C.x.array[[range(21*c,21*c+21) for c in cells]] = np.tile(C_ext, (len(cells),1))
    +# Same approach for density
    +Q = dolfinx.fem.FunctionSpace(mesh, ("DG", 0))
    +rho = dolfinx.fem.Function(Q)
    +cells = cell_tags.find(1) #core (tag=1)
    +rho.x.array[cells] = np.tile(rho_core, len(cells))
    +cells = cell_tags.find(2) #exterior (tag=2)
    +rho.x.array[cells] = np.tile(rho_ext, len(cells))
    +# Vizualization
    +plotter = pyvista.Plotter(window_size=[600, 400])
    +gridC = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(mesh, mesh.topology.dim)) #or *dolfinx.plot.create_vtk_mesh(Vmesh)
    +gridC.cell_data["Cij"] = C.x.array[0::21].real #index from 0 to 35, 0 being for C11...
    +#gridC.cell_data["Cij"] = rho.x.array.real #for checking density
    +gridC.set_active_scalars("Cij")
    +plotter.add_mesh(grid, style="wireframe", color="k") #FE mesh (with vertices of order 1 elements only owing to pyvista) 
    +plotter.add_mesh(gridC, opacity=0.8, show_scalar_bar=True, show_edges=False)
    +plotter.add_text('Re(Cij)', 'upper_edge', color='black', font_size=8)
    +plotter.view_xy()
    +plotter.show()
    +
    +##################################
    +# Create Cartesian PML functions, continuous
    +def eval_gamma(x): #pml profile: continuous and parabolic
    +    values = [1+3*(alpha-1)*((abs(x[0])-a)/(b-a))**2*(abs(x[0])>=a), #gammax
    +              1+3*(alpha-1)*((abs(x[1])-a)/(b-a))**2*(abs(x[1])>=a)] #gammay
    +    return values
    +Q = dolfinx.fem.FunctionSpace(mesh, ufl.VectorElement("CG", cell_type, 2, 2))
    +gamma = dolfinx.fem.Function(Q)
    +gamma.interpolate(eval_gamma)
    +# Vizualization
    +plotter = pyvista.Plotter(window_size=[800, 400], shape=(1,2))
    +gridx = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(Q))
    +gridx.point_data["gammax"] = gamma.x.array[0::2].imag
    +gridx.set_active_scalars("gammax")
    +plotter.subplot(0,0)
    +plotter.add_mesh(grid, style="wireframe", color="k") #FE mesh
    +plotter.add_mesh(gridx, opacity=0.8, show_scalar_bar=True, show_edges=False)
    +plotter.add_text('Im(gammax)', 'upper_edge', color='black', font_size=8)
    +plotter.view_xy()
    +gridy = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(Q))
    +gridy.point_data["gammay"] = gamma.x.array[1::2].imag
    +gridy.set_active_scalars("gammay")
    +plotter.subplot(0,1)
    +plotter.add_mesh(grid, style="wireframe", color="k") #FE mesh
    +plotter.add_mesh(gridy, opacity=0.8, show_scalar_bar=True, show_edges=False)
    +plotter.add_text('Im(gammay)', 'upper_edge', color='black', font_size=8)
    +plotter.view_xy()
    +plotter.show()
    +
    +##################################
    +# Create free boundary conditions (or uncomment lines below for Dirichlet)
    +# bcs = []
    +# Dirichlet test case:
    +boundary_facets = facet_tags.find(21) #outer boundary (tag=21)
    +boundary_dofs = dolfinx.fem.locate_dofs_topological(V=V, entity_dim=mesh.topology.dim-1, entities=boundary_facets)
    +bcs = [dolfinx.fem.dirichletbc(value=np.zeros(3, dtype=PETSc.ScalarType), dofs=boundary_dofs, V=V)]
    +
    +##################################
    +# Define variational problem (SAFE method)
    +#ufl.dx = ufl.dx(metadata={"quadrature_rule": "GLL", "quadrature_degree": 2*(8-1)}) if gll else ufl.dx #uncomment to build diagonal mass matrix
    +u = ufl.TrialFunction(V)
    +v = ufl.TestFunction(V)
    +Lx = lambda u: ufl.as_vector([u[0].dx(0), 0, 0, u[1].dx(0), u[2].dx(0), 0])
    +Ly = lambda u: ufl.as_vector([0, u[1].dx(1), 0, u[0].dx(1), 0, u[2].dx(1)])
    +Lz  = lambda u: ufl.as_vector([0, 0, u[2], 0, u[0], u[1]])
    +gammax = gamma[0]
    +gammay = gamma[1]
    +k0 = (ufl.inner(C*(gammay/gammax*Lx(u)+Ly(u)), Lx(v)) + ufl.inner(C*(Lx(u)+gammax/gammay*Ly(u)), Ly(v))) * ufl.dx
    +k0_form = dolfinx.fem.form(k0)
    +k1 = (gammay*ufl.inner(C*Lz(u), Lx(v))+gammax*ufl.inner(C*Lz(u), Ly(v))) * ufl.dx
    +k1_form = dolfinx.fem.form(k1)
    +k2 = ufl.inner(C*Lz(u), Lz(v)) * gammax * gammay * ufl.dx
    +k2_form = dolfinx.fem.form(k2)
    +m = rho * ufl.inner(u, v) * gammax * gammay * ufl.dx
    +mass_form = dolfinx.fem.form(m)
    +
    +##################################
    +# Build PETSc matrices
    +M = dolfinx.fem.petsc.assemble_matrix(mass_form, bcs=bcs, diagonal=0.0)
    +M.assemble()
    +K0 = dolfinx.fem.petsc.assemble_matrix(k0_form, bcs=bcs)
    +K0.assemble()
    +K1 = dolfinx.fem.petsc.assemble_matrix(k1_form, bcs=bcs, diagonal=0.0)
    +K1.assemble()
    +K2 = dolfinx.fem.petsc.assemble_matrix(k2_form, bcs=bcs, diagonal=0.0)
    +K2.assemble()
    +
    +##################################
    +# Solve the eigenproblem with SLEPc\
    +# The parameter is omega, the eigenvalue is k
    +wg = Waveguide(MPI.COMM_WORLD, M, K0, K1, K2)
    +#(K2, M) = (wg._diag(K2.getDiagonal()), wg._diag(M.getDiagonal())) if gll else (K2, M) ##uncomment to save allocated memory of K2 and M if they are to be diagonal
    +wg.set_parameters(omega=omega)
    +wg.evp.setWhichEigenpairs(SLEPc.PEP.Which.TARGET_MAGNITUDE) #here, preferred to TARGET_IMAGINARY
    +wg.solve(nev, target=target)
    +
    +##################################
    +# Plot dispersion curves\
    +# Results are to be compared with Fig. 8 of Treyssede, Journal of Computational Physics 314 (2016), 341–354
    +wg.plot_scaler["energy_velocity"] = L0/T0/1000 #units in m/ms
    +wg.plot_scaler["frequency"] = L0/T0/1000 #frequency units in MHz-mm
    +sc = wg.plot_energy_velocity()
    +sc.axes.set_xlim([0, 26])
    +sc.axes.set_ylim([0, 6])
    +sc.axes.set_xlabel('Frequency-halfwidth (MHz-mm)')
    +sc.axes.set_ylabel('Energy velocity (m/ms)')
    +#plt.savefig("buried_bar_energy_velocity.png")
    +wg.plot_scaler["attenuation"] = 8.686*1000 #units in dB-mm/m
    +sc = wg.plot_attenuation()
    +sc.axes.set_xlim([0, 26])
    +sc.axes.set_ylim([0, 2000])
    +sc.axes.set_xlabel('Frequency-halfwidth (MHz-mm)')
    +sc.axes.set_ylabel('Attenuation (dB-mm/m)')
    +#plt.savefig("buried_bar_attenuation.png")
    +plt.show()
    +#wg.plot_spectrum(index=0)
    +#plt.show()
    +
    +##################################
    +# Mode shape visualization
    +ik, imode = 345, 0 #parameter index, mode index to visualize
    +print(f"f={wg.omega[ik]*L0/T0/1000/(2*np.pi):1.1f}MHz-mm, attenuation={8.686*1000*wg.eigenvalues[ik][imode].imag:1.2f}dB-mm/m")
    +vec = wg.eigenvectors[ik].getColumnVector(imode)
    +u_grid = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(V))
    +u_grid["u"] = np.array(vec).real.reshape(int(np.array(vec).size/V.element.value_shape), int(V.element.value_shape)) #V.element.value_shape is equal to 3
    +u_plotter = pyvista.Plotter()
    +u_plotter.add_mesh(grid, style="wireframe", color="k") #FE mesh
    +u_plotter.add_mesh(u_grid.warp_by_vector("u", factor=1), opacity=1, show_scalar_bar=False, show_edges=False, cmap='viridis') #do not show edges of higher order elements with pyvista
    +#u_plotter.show_axes()
    +u_plotter.screenshot('buried_bar_mode_shape.png', transparent_background=True)
    +u_plotter.show()
    +
    +""
    +
    +
    +
    +
    +
    +

    4. Excitation of a three-dimensional elastic bar of circular cross-section

    +

    3D elastic waveguide example. +The cross-section is a 2D circle with free boundary conditions on its 1D boundaries, material: elastic steel. +The eigenproblem is solved with the varying parameter as the frequency (eigenvalues are then wavenumbers). +The forced response is computed for a point force at the center node ot the cross-section. +Results are to be compared with Figs. 5, 6 and 7 of paper: Treyssede, Wave Motion 87 (2019), 75-91.

    +
    # This file is a tutorial for waveguicsx (*), whose inputs are
    +# the matrices K0, K1, K2, M and the excitation vector F.
    +# In the tutorial, K0, K1, K2 and M are finite element matrices generated by FEnicSX (**).
    +#  (*) waveguicsx is a python library for solving complex waveguide problems
    +#      Copyright (C) 2023-2024  Fabien Treyssede
    +#      waveguicsx is free software distributed under the GNU General Public License
    +#      (https://github.com/treyssede/waveguicsx)
    +# (**) FEniCSx is an open-source computing platform for solving partial differential equations
    +#      distributed under the GNU Lesser General Public License (https://fenicsproject.org/)
    +
    +##################################
    +# 3D elastic waveguide example\
    +# The cross-section is a 2D circle with free boundary conditions on its 1D boundaries, material: elastic steel\
    +# The waveguide FE formulation (SAFE) leads to the following eigenvalue problem:\
    +# $(\textbf{K}_1-\omega^2\textbf{M}+\text{i}k(\textbf{K}_2+\textbf{K}_2^\text{T})+k^2\textbf{K}_3)\textbf{U}=\textbf{0}$\
    +# This eigenproblem is solved with the varying parameter as the frequency (eigenvalues are then wavenumbers).\
    +# The forced response is computed for a point force at the center node ot the cross-section.\
    +# Results are to be compared with Figs. 5, 6 and 7 of paper: Treyssede, Wave Motion 87 (2019), 75-91.
    +
    +import gmsh #full documentation entirely defined in the `gmsh.py' module
    +import dolfinx
    +import ufl
    +from mpi4py import MPI
    +from petsc4py import PETSc
    +from slepc4py import SLEPc
    +import numpy as np
    +import matplotlib.pyplot as plt
    +import pyvista
    +
    +from waveguicsx.waveguide import Waveguide
    +#For proper use with a jupyter notebook, uncomment the following line:
    +#pyvista.set_jupyter_backend("static"); pyvista.start_xvfb() #try: "none", "static", "pythreejs", "ipyvtklink"...
    +
    +##################################
    +# Input parameters
    +a = 2.7e-3 #cross-section radius (m)
    +le = a/8 #finite element characteristic length (m)
    +rho, cs, cl = 7800, 3296, 5963 #density (kg/m3), shear and longitudinal wave celerities (m/s)
    +kappas, kappal = 0*0.008, 0*0.003 #shear and longitudinal bulk wave attenuations (Np/wavelength)
    +omega = np.arange(0.1, 10.1, 0.1)*cs/a #angular frequencies (rad/s)
    +nev = 60 #number of eigenvalues
    +
    +##################################
    +# Re-scaling
    +L0 = a #characteristic length
    +T0 = a/cs #characteristic time
    +M0 = rho*a**3#characteristic mass
    +a, le = a/L0, le/L0
    +rho, cs, cl = rho/M0*L0**3, cs/L0*T0, cl/L0*T0
    +omega = omega*T0
    +cs, cl = cs/(1+1j*kappas/2/np.pi), cl/(1+1j*kappal/2/np.pi) #complex celerities
    +
    +##################################
    +# Create mesh from Gmsh and finite elements (six-node triangles with three dofs per node for the three components of displacement)
    +gmsh.initialize()
    +# Core
    +origin = gmsh.model.geo.addPoint(+0, 0, 0, le, 1)
    +gmsh.model.geo.addPoint(+a, 0, 0, le, 2)
    +gmsh.model.geo.addPoint(0, +a, 0, le, 3)
    +gmsh.model.geo.addPoint(-a, 0, 0, le, 4)
    +gmsh.model.geo.addPoint(0, -a, 0, le, 5)
    +gmsh.model.geo.addCircleArc(2, 1, 3, 1)
    +gmsh.model.geo.addCircleArc(3, 1, 4, 2)
    +gmsh.model.geo.addCircleArc(4, 1, 5, 3)
    +gmsh.model.geo.addCircleArc(5, 1, 2, 4)
    +gmsh.model.geo.addCurveLoop([1, 2, 3, 4], 1)
    +disk = gmsh.model.geo.addPlaneSurface([1])
    +# Physical groups
    +gmsh.model.geo.synchronize() #the CAD entities must be synchronized with the Gmsh model
    +gmsh.model.addPhysicalGroup(2, [disk], tag=1) #2: for 2D (surface)
    +#gmsh.model.addPhysicalGroup(1, [1, 2, 3, 4], tag=11) #1: for 1D (line)
    +# Generate mesh
    +gmsh.model.mesh.embed(0, [origin], 2, disk) #ensure node points at the origin
    +gmsh.model.mesh.generate(2) #generate a 2D mesh
    +gmsh.model.mesh.setOrder(2) #interpolation order for the geometry, here 2nd order
    +# From gmsh to fenicsx
    +mesh, cell_tags, facet_tags = dolfinx.io.gmshio.model_to_mesh(gmsh.model, MPI.COMM_WORLD, 0, gdim=2)
    +# # Reminder for save & read
    +# gmsh.write("Elastic_Waveguide_Bar3D_Open.msh") #save to disk
    +# mesh, cell_tags, facet_tags = dolfinx.io.gmshio.read_from_msh("Elastic_Waveguide_Bar3D_Open.msh", MPI.COMM_WORLD, rank=0, gdim=2)
    +gmsh.finalize() #called when done using the Gmsh Python API
    +# Visualize FE mesh with pyvista
    +Vmesh = dolfinx.fem.FunctionSpace(mesh, ufl.FiniteElement("CG", "triangle", 1)) #order 1 is properly handled with pyvista
    +plotter = pyvista.Plotter()
    +grid = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(Vmesh))
    +grid.cell_data["Marker"] = cell_tags.values
    +grid.set_active_scalars("Marker")
    +plotter.add_mesh(grid, show_edges=True)
    +plotter.view_xy()
    +plotter.show()
    +# Finite element space
    +element = ufl.VectorElement("CG", "triangle", 2, 3) #Lagrange element, triangle, quadratic "P2", 3D vector
    +V = dolfinx.fem.FunctionSpace(mesh, element)
    +
    +##################################
    +# Create Material properties (isotropic)
    +def isotropic_law(rho, cs, cl):
    +    E, nu = rho*cs**2*(3*cl**2-4*cs**2)/(cl**2-cs**2), 0.5*(cl**2-2*cs**2)/(cl**2-cs**2)
    +    C11 = C22 = C33 = E/(1+nu)/(1-2*nu)*(1-nu)
    +    C12 = C13 = C23 = E/(1+nu)/(1-2*nu)*nu
    +    C44 = C55 = C66 = E/(1+nu)/2
    +    return np.array([[C11,C12,C13,0,0,0], 
    +                     [C12,C22,C23,0,0,0], 
    +                     [C13,C23,C33,0,0,0], 
    +                     [0,0,0,C44,0,0], 
    +                     [0,0,0,0,C55,0], 
    +                     [0,0,0,0,0,C66]])
    +C = isotropic_law(rho, cs, cl)
    +C = dolfinx.fem.Constant(mesh, PETSc.ScalarType(C))
    +
    +##################################
    +# Create free boundary conditions
    +bcs = []
    +
    +##################################
    +# Define variational problem (SAFE method)
    +u = ufl.TrialFunction(V)
    +v = ufl.TestFunction(V)
    +Lxy = lambda u: ufl.as_vector([u[0].dx(0), u[1].dx(1), 0, u[0].dx(1)+u[1].dx(0), u[2].dx(0), u[2].dx(1)])
    +Lz  = lambda u: ufl.as_vector([0, 0, u[2], 0, u[0], u[1]])
    +k0 = ufl.inner(C*Lxy(u), Lxy(v)) * ufl.dx
    +k0_form = dolfinx.fem.form(k0)
    +k1 = ufl.inner(C*Lz(u), Lxy(v)) * ufl.dx
    +k1_form = dolfinx.fem.form(k1)
    +k2 = ufl.inner(C*Lz(u), Lz(v)) * ufl.dx
    +k2_form = dolfinx.fem.form(k2)
    +m = rho*ufl.inner(u, v) * ufl.dx
    +mass_form = dolfinx.fem.form(m)
    +
    +##################################
    +# Build PETSc matrices
    +M = dolfinx.fem.petsc.assemble_matrix(mass_form, bcs=bcs, diagonal=0.0)
    +M.assemble()
    +K0 = dolfinx.fem.petsc.assemble_matrix(k0_form, bcs=bcs)
    +K0.assemble()
    +K1 = dolfinx.fem.petsc.assemble_matrix(k1_form, bcs=bcs, diagonal=0.0)
    +K1.assemble()
    +K2 = dolfinx.fem.petsc.assemble_matrix(k2_form, bcs=bcs, diagonal=0.0)
    +K2.assemble()
    +
    +##################################
    +# Solve the eigenproblem with SLEPc (the parameter is omega, the eigenvalue is k)
    +wg = Waveguide(MPI.COMM_WORLD, M, K0, K1, K2)
    +wg.set_parameters(omega=omega, two_sided=False)
    +wg.evp.setTolerances(tol=1e-10, max_it=20)
    +wg.solve(nev=nev, target=0) #access to components with: wg.eigenvalues[ik][imode], wg.eigenvectors[ik][idof,imode]
    +
    +##################################
    +# Plot dispersion curves\
    +# Results are to be compared with Fig. 5 of Treyssede, Wave Motion 87 (2019), 75-91
    +wg.plot_energy_velocity(direction=+1)
    +plt.show()
    +
    +##################################
    +# Excitation force definition (point force)
    +dof_coords = V.tabulate_dof_coordinates()
    +x0 = np.array([0, 0, 0]) #desired coordinate of point force
    +dof = int(np.argmin(np.linalg.norm(dof_coords - x0, axis=1))) #find nearest dof
    +print(f'Point force coordinates (nearest dof):  {(dof_coords[dof,:])}') #check
    +F0 = M.createVecRight()
    +dof = dof*3 + 2 #orientation along z
    +#dof = dof - 2 #uncomment this line for an orientation along x instead
    +F0[dof] = 1
    +##Uncomment lines below for distributed excitation
    +#body_force = dolfinx.fem.Constant(mesh, PETSc.ScalarType((0, 0, 1))) #body force of unit amplitude along z
    +#traction = dolfinx.fem.Constant(mesh, PETSc.ScalarType((0, 0, 0))) #zero traction
    +#ds = ufl.Measure("ds", domain=mesh)
    +#f = ufl.inner(body_force, v) * ufl.dx + ufl.inner(traction, v) * ds
    +#f_form = dolfinx.fem.form(f)
    +#F = dolfinx.fem.petsc.assemble_vector(f_form)
    +#F.assemble()
    +
    +##################################
    +# Computation of excitability and forced response\
    +# Results are to be compared with Figs. 6 and 7 of Treyssede, Wave Motion 87 (2019), 75-91
    +wg.compute_response_coefficient(F=F0, dof=dof)
    +wg.plot_coefficient()
    +sc = wg.plot_excitability()
    +sc.axes.set_yscale('log')
    +sc.axes.set_ylim(1e-3,0.5e+1)
    +frequency, response, axs = wg.compute_response(dof=dof, z=[5], spectrum=None, plot=True) #spectrum=excitation.spectrum
    +axs[0].set_yscale('log')
    +axs[0].set_ylim(1e-2,1e+1)
    +axs[0].get_lines()[0].set_color("black")
    +plt.close()
    +
    +##################################
    +# Mode shape visualization
    +ik, imode = 50, 1 #parameter index, mode index to visualize
    +vec = wg.eigenvectors[ik].getColumnVector(imode)
    +u_grid = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(V))
    +u_grid["u"] = np.array(vec).real.reshape(int(np.array(vec).size/V.element.value_shape), int(V.element.value_shape)) #V.element.value_shape is equal to 3
    +u_plotter = pyvista.Plotter()
    +u_plotter.add_mesh(grid, style="wireframe", color="k") #FE mesh
    +u_plotter.add_mesh(u_grid.warp_by_vector("u", factor=0.5), opacity=0.8, show_scalar_bar=True, show_edges=False) #do not show edges of higher order elements with pyvista
    +u_plotter.show_axes()
    +u_plotter.show()
    +
    +
    +""
    +
    +
    +
    +
    +
    +

    5. Excitation of a three-dimensional elastic bar of circular cross-section with parallelization

    +

    3D elastic waveguide example. +The cross-section is a 2D circle with free boundary conditions on its 1D boundaries, material: elastic steel. +The eigenproblem is solved with the varying parameter as the frequency (eigenvalues are then wavenumbers). +The forced response is computed for a point force at the center node ot the cross-section. +Results are to be compared with Figs. 5, 6 and 7 of paper: Treyssede, Wave Motion 87 (2019), 75-91. +In this example:

    +
      +
    • the parameter loop (here, the frequency loop) is distributed on all processes

    • +
    • FE mesh and matrices are built on each local process

    • +
    +
    # This file is a tutorial for waveguicsx (*), whose inputs are
    +# the matrices K0, K1, K2, M and the excitation vector F.
    +# In the tutorial, K0, K1, K2 and M are finite element matrices generated by FEnicSX (**).
    +#  (*) waveguicsx is a python library for solving complex waveguide problems
    +#      Copyright (C) 2023-2024  Fabien Treyssede
    +#      waveguicsx is free software distributed under the GNU General Public License
    +#      (https://github.com/treyssede/waveguicsx)
    +# (**) FEniCSx is an open-source computing platform for solving partial differential equations
    +#      distributed under the GNU Lesser General Public License (https://fenicsproject.org/)
    +
    +##################################
    +# 3D elastic waveguide example\
    +# The cross-section is a 2D circle with free boundary conditions on its 1D boundaries, material: elastic steel\
    +# The waveguide FE formulation (SAFE) leads to the following eigenvalue problem:\
    +# $(\textbf{K}_1-\omega^2\textbf{M}+\text{i}k(\textbf{K}_2+\textbf{K}_2^\text{T})+k^2\textbf{K}_3)\textbf{U}=\textbf{0}$\
    +# This eigenproblem is solved with the varying parameter as the frequency (eigenvalues are then wavenumbers).\
    +# The forced response is computed for a point force at the center node ot the cross-section.\
    +# Results are to be compared with Figs. 5, 6 and 7 of paper: Treyssede, Wave Motion 87 (2019), 75-91.
    +# In this example:
    +# - the parameter loop (here, the frequency loop) is distributed on all processes
    +# - FE mesh and matrices are built on each local process
    +# Reminder for an execution in parallel mode (e.g. 8 processes):
    +#  mpiexec -n 8 python3 Elastic_Waveguide_CircularBar3D_ForcedResponse_Gmsh_ParallelizedLoop.py
    +
    +import gmsh #full documentation entirely defined in the `gmsh.py' module
    +import dolfinx
    +import ufl
    +from mpi4py import MPI
    +from petsc4py import PETSc
    +from slepc4py import SLEPc
    +import numpy as np
    +import matplotlib.pyplot as plt
    +#import pyvista
    +
    +from waveguicsx.waveguide import Waveguide
    +#For proper use with a jupyter notebook, uncomment the following line:
    +#pyvista.set_jupyter_backend("static"); pyvista.start_xvfb() #try: "none", "static", "pythreejs", "ipyvtklink"...
    +
    +##################################
    +# Input parameters
    +a = 2.7e-3 #cross-section radius (m)
    +le = a/8 #finite element characteristic length (m)
    +rho, cs, cl = 7800, 3296, 5963 #density (kg/m3), shear and longitudinal wave celerities (m/s)
    +kappas, kappal = 0*0.008, 0*0.003 #shear and longitudinal bulk wave attenuations (Np/wavelength)
    +omega = np.arange(0.1, 10.1, 0.1)*cs/a #angular frequencies (rad/s)
    +nev = 300 #number of eigenvalues
    +
    +##################################
    +# Re-scaling
    +L0 = a #characteristic length
    +T0 = a/cs #characteristic time
    +M0 = rho*a**3#characteristic mass
    +a, le = a/L0, le/L0
    +rho, cs, cl = rho/M0*L0**3, cs/L0*T0, cl/L0*T0
    +omega = omega*T0
    +cs, cl = cs/(1+1j*kappas/2/np.pi), cl/(1+1j*kappal/2/np.pi) #complex celerities
    +
    +##################################
    +# Create mesh from Gmsh and finite elements (six-node triangles with three dofs per node for the three components of displacement)
    +gmsh.initialize()
    +# Core
    +origin = gmsh.model.geo.addPoint(+0, 0, 0, le, 1)
    +gmsh.model.geo.addPoint(+a, 0, 0, le, 2)
    +gmsh.model.geo.addPoint(0, +a, 0, le, 3)
    +gmsh.model.geo.addPoint(-a, 0, 0, le, 4)
    +gmsh.model.geo.addPoint(0, -a, 0, le, 5)
    +gmsh.model.geo.addCircleArc(2, 1, 3, 1)
    +gmsh.model.geo.addCircleArc(3, 1, 4, 2)
    +gmsh.model.geo.addCircleArc(4, 1, 5, 3)
    +gmsh.model.geo.addCircleArc(5, 1, 2, 4)
    +gmsh.model.geo.addCurveLoop([1, 2, 3, 4], 1)
    +disk = gmsh.model.geo.addPlaneSurface([1])
    +# Physical groups
    +gmsh.model.geo.synchronize() #the CAD entities must be synchronized with the Gmsh model
    +gmsh.model.addPhysicalGroup(2, [disk], tag=1) #2: for 2D (surface)
    +#gmsh.model.addPhysicalGroup(1, [1, 2, 3, 4], tag=11) #1: for 1D (line)
    +# Generate mesh
    +gmsh.model.mesh.embed(0, [origin], 2, disk) #ensure node points at the origin
    +gmsh.model.mesh.generate(2) #generate a 2D mesh
    +gmsh.model.mesh.setOrder(2) #interpolation order for the geometry, here 2nd order
    +# From gmsh to fenicsx
    +mesh, cell_tags, facet_tags = dolfinx.io.gmshio.model_to_mesh(gmsh.model, MPI.COMM_SELF, 0, gdim=2) #MPI.COMM_SELF = FE mesh is built on each local process
    +# # Reminder for save & read
    +# gmsh.write("Elastic_Waveguide_Bar3D_Open.msh") #save to disk
    +# mesh, cell_tags, facet_tags = dolfinx.io.gmshio.read_from_msh("Elastic_Waveguide_Bar3D_Open.msh", MPI.COMM_WORLD, rank=0, gdim=2)
    +gmsh.finalize() #called when done using the Gmsh Python API
    +# Finite element space
    +element = ufl.VectorElement("CG", "triangle", 2, 3) #Lagrange element, triangle, quadratic "P2", 3D vector
    +V = dolfinx.fem.FunctionSpace(mesh, element)
    +
    +##################################
    +# Create Material properties (isotropic)
    +def isotropic_law(rho, cs, cl):
    +    E, nu = rho*cs**2*(3*cl**2-4*cs**2)/(cl**2-cs**2), 0.5*(cl**2-2*cs**2)/(cl**2-cs**2)
    +    C11 = C22 = C33 = E/(1+nu)/(1-2*nu)*(1-nu)
    +    C12 = C13 = C23 = E/(1+nu)/(1-2*nu)*nu
    +    C44 = C55 = C66 = E/(1+nu)/2
    +    return np.array([[C11,C12,C13,0,0,0], 
    +                     [C12,C22,C23,0,0,0], 
    +                     [C13,C23,C33,0,0,0], 
    +                     [0,0,0,C44,0,0], 
    +                     [0,0,0,0,C55,0], 
    +                     [0,0,0,0,0,C66]])
    +C = isotropic_law(rho, cs, cl)
    +C = dolfinx.fem.Constant(mesh, PETSc.ScalarType(C))
    +
    +##################################
    +# Create free boundary conditions
    +bcs = []
    +
    +##################################
    +# Define variational problem (SAFE method)
    +u = ufl.TrialFunction(V)
    +v = ufl.TestFunction(V)
    +Lxy = lambda u: ufl.as_vector([u[0].dx(0), u[1].dx(1), 0, u[0].dx(1)+u[1].dx(0), u[2].dx(0), u[2].dx(1)])
    +Lz  = lambda u: ufl.as_vector([0, 0, u[2], 0, u[0], u[1]])
    +k0 = ufl.inner(C*Lxy(u), Lxy(v)) * ufl.dx
    +k0_form = dolfinx.fem.form(k0)
    +k1 = ufl.inner(C*Lz(u), Lxy(v)) * ufl.dx
    +k1_form = dolfinx.fem.form(k1)
    +k2 = ufl.inner(C*Lz(u), Lz(v)) * ufl.dx
    +k2_form = dolfinx.fem.form(k2)
    +m = rho*ufl.inner(u, v) * ufl.dx
    +mass_form = dolfinx.fem.form(m)
    +
    +##################################
    +# Build PETSc matrices
    +M = dolfinx.fem.petsc.assemble_matrix(mass_form, bcs=bcs, diagonal=0.0)
    +M.assemble()
    +K0 = dolfinx.fem.petsc.assemble_matrix(k0_form, bcs=bcs)
    +K0.assemble()
    +K1 = dolfinx.fem.petsc.assemble_matrix(k1_form, bcs=bcs, diagonal=0.0)
    +K1.assemble()
    +K2 = dolfinx.fem.petsc.assemble_matrix(k2_form, bcs=bcs, diagonal=0.0)
    +K2.assemble()
    +
    +##################################
    +# Excitation force definition (point force)
    +dof_coords = V.tabulate_dof_coordinates()
    +x0 = np.array([0, 0, 0]) #desired coordinate of point force
    +dof = int(np.argmin(np.linalg.norm(dof_coords - x0, axis=1))) #find nearest dof
    +print(f'Point force coordinates (nearest dof):  {(dof_coords[dof,:])}') #check
    +F0 = M.createVecRight()
    +dof = dof*3 + 2 #orientation along z
    +dof = dof - 2 #uncomment this line for an orientation along x instead
    +F0[dof] = 1
    +##Uncomment lines below for distributed excitation
    +#body_force = dolfinx.fem.Constant(mesh, PETSc.ScalarType((0, 0, 1))) #body force of unit amplitude along z
    +#traction = dolfinx.fem.Constant(mesh, PETSc.ScalarType((0, 0, 0))) #zero traction
    +#ds = ufl.Measure("ds", domain=mesh)
    +#f = ufl.inner(body_force, v) * ufl.dx + ufl.inner(traction, v) * ds
    +#f_form = dolfinx.fem.form(f)
    +#F = dolfinx.fem.petsc.assemble_vector(f_form)
    +#F.assemble()
    +
    +##################################
    +# Parallelization
    +comm = MPI.COMM_WORLD #use all processes for the loop
    +size = comm.Get_size()  #number of processors
    +rank = comm.Get_rank()  #returns the rank of the process that called it within comm_world
    +# Split the parameter range and scatter to all
    +if rank == 0: #define on rank 0 only
    +    omega_split = np.array_split(omega, size) #split param in blocks of length size roughly
    +else:
    +    omega_split = None
    +omega_local = comm.scatter(omega_split, root=0) #scatter 1 block per process
    +
    +##################################
    +# Solve the eigenproblem with SLEPc (the parameter is omega, the eigenvalue is k)
    +wg = Waveguide(MPI.COMM_SELF, M, K0, K1, K2) #MPI.COMM_SELF = SLEPc will used FE matrices on each local process
    +wg.set_parameters(omega=omega_local)
    +wg.evp.setTolerances(tol=1e-10, max_it=20)
    +wg.solve(nev=nev, target=0) #access to components with: wg.eigenvalues[ik][imode], wg.eigenvectors[ik][idof,imode]
    +
    +##################################
    +# Computation of excitability and forced response
    +wg.compute_response_coefficient(F=F0, dof=dof)
    +frequency, response = wg.compute_response(dof=dof, z=5, spectrum=None) #spectrum=excitation.spectrum
    +
    +##################################
    +# Gather
    +wg.omega = comm.reduce([wg.omega], op=MPI.SUM, root=0) #reduce works for lists: brackets are necessary (wg.omega is not a list but a numpy array)
    +wg.eigenvalues = comm.reduce(wg.eigenvalues, op=MPI.SUM, root=0)
    +#wg.eigenvectors = comm.reduce(wg.eigenvectors, op=MPI.SUM, root=0) #don't do this line: reduce cannot pickle 'petsc4py.PETSc.Vec' objects (keep the mode shapes distributed on each processor rather than gather them)
    +wg.coefficient = comm.reduce(wg.coefficient, op=MPI.SUM, root=0)
    +wg.excitability = comm.reduce(wg.excitability, op=MPI.SUM, root=0)
    +frequency = comm.reduce([frequency], op=MPI.SUM, root=0)
    +response = comm.reduce([response], op=MPI.SUM, root=0)
    +
    +##################################
    +# Plots
    +if rank == 0:
    +    wg.omega = np.concatenate(wg.omega) #wg.omega is transformed to a numpy array for a proper use of wg.plot()
    +    wg.plot()
    +    wg.plot_coefficient()
    +    sc = wg.plot_excitability()
    +    sc.axes.set_yscale('log')
    +    sc.axes.set_ylim(1e-3,0.5e+1)
    +    frequency = np.concatenate(frequency)
    +    response = np.concatenate(response) #use np.concatenate(response, axis=1)  if z contains more than one value
    +    fig, ax = plt.subplots(1, 1)  
    +    ax.plot(frequency, np.abs(response.T), linewidth=1, linestyle="-", color="k")
    +    ax.set_xlabel('frequency')
    +    ax.set_ylabel('|u|')
    +    ax.set_yscale('log')
    +    ax.set_ylim(1e-2,1e+1)
    +    fig.tight_layout()
    +    #plt.savefig("figure_name.svg")
    +    plt.show()
    +
    +
    +
    +
    +
    +

    6. Time response of a two-dimensional plate excited near its first ZGV resonance

    +

    2D (visco-)elastic waveguide example (Lamb modes in a plate excited near 1st ZGV resonance). +The cross-section is a 1D line with free boundary conditions on its boundaries. +Material: viscoelastic steel. +The eigenproblem is solved with the varying parameter as the frequency (eigenvalues are then wavenumbers). +Viscoelastic loss can be included by introducing imaginary parts (negative) to wave celerities. +Results are to be compared with Figs. 5b, 7a and 8a of paper: Treyssede and Laguerre, JASA 133 (2013), 3827-3837. +Note: the depth direction is x, the axis of propagation is z.

    +
    # This file is a tutorial for waveguicsx (*), whose inputs are
    +# the matrices K0, K1, K2, M and the excitation vector F.
    +# In the tutorial, K0, K1, K2 and M are finite element matrices generated by FEnicSX (**).
    +#  (*) waveguicsx is a python library for solving complex waveguide problems
    +#      Copyright (C) 2023-2024  Fabien Treyssede
    +#      waveguicsx is free software distributed under the GNU General Public License
    +#      (https://github.com/treyssede/waveguicsx)
    +# (**) FEniCSx is an open-source computing platform for solving partial differential equations
    +#      distributed under the GNU Lesser General Public License (https://fenicsproject.org/)
    +
    +##################################
    +# 2D (visco-)elastic waveguide example (Lamb modes in a plate excited near 1st ZGV resonance)\
    +# The cross-section is a 1D line with free boundary conditions on its boundaries\
    +# material: viscoelastic steel\
    +# The waveguide FE formulation (SAFE) leads to the following eigenvalue problem:\
    +# $(\textbf{K}_1-\omega^2\textbf{M}+\text{i}k(\textbf{K}_2+\textbf{K}_2^\text{T})+k^2\textbf{K}_3)\textbf{U}=\textbf{0}$\
    +# This eigenproblem is solved with the varying parameter as the frequency (eigenvalues are then wavenumbers)\
    +# Viscoelastic loss can be included by introducing imaginary parts (negative) to wave celerities\
    +# Results are to be compared with Figs. 5b, 7a and 8a of paper: Treyssede and Laguerre, JASA 133 (2013), 3827-3837\
    +# Note: the depth direction is x, the axis of propagation is z
    +
    +import dolfinx
    +import ufl
    +from mpi4py import MPI
    +from petsc4py import PETSc
    +from slepc4py import SLEPc
    +import numpy as np
    +import matplotlib.pyplot as plt
    +import pyvista
    +
    +from waveguicsx.waveguide import Waveguide, Signal
    +#For proper use with a jupyter notebook, uncomment the following line:
    +#pyvista.set_jupyter_backend("static"); pyvista.start_xvfb() #try: "none", "static", "pythreejs", "ipyvtklink"...
    +
    +##################################
    +# Input parameters
    +h = 0.01 #plate thickness (m)
    +N = 10 #number of finite elements along one half-side
    +rho, cs, cl = 7800, 3218, 6020 #core density (kg/m3), shear and longitudinal wave celerities (m/s)
    +kappas, kappal = 0*0.008, 0*0.003 #core shear and longitudinal bulk wave attenuations (Np/wavelength)
    +nev = 20 #number of eigenvalues
    +
    +##################################
    +# Excitation spectrum
    +excitation = Signal(alpha=0*np.log(50)/5e-3)
    +excitation.toneburst(fs=1000e3, T=5e-3, fc=250e3, n=5)
    +excitation.plot()
    +excitation.plot_spectrum()
    +#excitation.ifft(coeff=1); excitation.plot() #uncomment for check only
    +plt.show()
    +omega = 2*np.pi*excitation.frequency #angular frequency range (rad/s)
    +
    +##################################
    +# Re-scaling
    +L0 = h #characteristic length
    +T0 = h/cs #characteristic time
    +M0 = rho*h**3#characteristic mass
    +h = h/L0
    +rho, cs, cl = rho/M0*L0**3, cs/L0*T0, cl/L0*T0
    +omega = omega*T0
    +cs, cl = cs/(1+1j*kappas/2/np.pi), cl/(1+1j*kappal/2/np.pi) #complex celerities (core)
    +
    +##################################
    +# Create mesh and finite elements (three-node lines with two dofs per node for the two components of displacement)
    +mesh = dolfinx.mesh.create_interval(MPI.COMM_WORLD, N, np.array([0, h]))
    +element = ufl.VectorElement("CG", "interval", 2, 2) #Lagrange element, line element, quadratic "P2", 2D vector
    +V = dolfinx.fem.FunctionSpace(mesh, element)
    +
    +##################################
    +# Create Material properties (isotropic)
    +def isotropic_law(rho, cs, cl):
    +    E, nu = rho*cs**2*(3*cl**2-4*cs**2)/(cl**2-cs**2), 0.5*(cl**2-2*cs**2)/(cl**2-cs**2)
    +    C11 = C22 = E/(1+nu)/(1-2*nu)*(1-nu)
    +    C12 = E/(1+nu)/(1-2*nu)*nu
    +    C33 = E/(1+nu)/2
    +    return ((C11,C12,0), 
    +            (C12,C22,0), 
    +            (0,0,C33))
    +C = isotropic_law(rho, cs, cl)
    +C = dolfinx.fem.Constant(mesh, PETSc.ScalarType(C))
    +
    +##################################
    +# Create free boundary conditions
    +bcs = []
    +
    +##################################
    +# Define variational problem (SAFE method)
    +u = ufl.TrialFunction(V)
    +v = ufl.TestFunction(V)
    +Lxy = lambda u: ufl.as_vector([u[0].dx(0), 0, u[1].dx(0)])
    +Lz = lambda u: ufl.as_vector([0, u[1], u[0]])
    +k0 = ufl.inner(C*Lxy(u), Lxy(v)) * ufl.dx
    +k0_form = dolfinx.fem.form(k0)
    +k1 = ufl.inner(C*Lz(u), Lxy(v)) * ufl.dx
    +k1_form = dolfinx.fem.form(k1)
    +k2 = ufl.inner(C*Lz(u), Lz(v)) * ufl.dx
    +k2_form = dolfinx.fem.form(k2)
    +m = rho*ufl.inner(u, v) * ufl.dx
    +mass_form = dolfinx.fem.form(m)
    +
    +##################################
    +# Build PETSc matrices
    +M = dolfinx.fem.petsc.assemble_matrix(mass_form, bcs=bcs, diagonal=0.0)
    +M.assemble()
    +K0 = dolfinx.fem.petsc.assemble_matrix(k0_form, bcs=bcs)
    +K0.assemble()
    +K1 = dolfinx.fem.petsc.assemble_matrix(k1_form, bcs=bcs, diagonal=0.0)
    +K1.assemble()
    +K2 = dolfinx.fem.petsc.assemble_matrix(k2_form, bcs=bcs, diagonal=0.0)
    +K2.assemble()
    +
    +##################################
    +# Solve the eigenproblem with SLEPc\
    +# The parameter is omega, the eigenvalue is k
    +wg = Waveguide(MPI.COMM_WORLD, M, K0, K1, K2)
    +wg.set_parameters(omega=omega)
    +wg.solve(nev) #access to components with: wg.eigenvalues[ik][imode], wg.eigenvectors[ik][idof,imode]
    +wg.plot()
    +plt.show() #blocking
    +
    +###############################################################################
    +# Excitation force: point force at x=0 oriented in the x-direction
    +dof_coords = V.tabulate_dof_coordinates()
    +x0 = np.array([0, 0, 0]) #desired coordinate of point force
    +dof = int(np.argmin(np.linalg.norm(dof_coords - x0, axis=1))) #find nearest dof
    +print(f'Point force coordinates (nearest dof):  {(dof_coords[dof,:])}') #check
    +F = M.createVecRight()
    +dof = dof*2 + 0 #x-direction
    +F[dof] = 1
    +
    +###############################################################################
    +# Computation of excitabilities and forced response\
    +# Results are to be compared with Figs. 5b and 7a of Treyssede and Laguerre, JASA 133 (2013), 3827-3837
    +wg.compute_response_coefficient(F=F, dof=dof)
    +wg.set_plot_scaler()
    +sc = wg.plot_excitability()
    +sc.axes.set_yscale('log')
    +sc.axes.set_ylim(1e-3,1e2)
    +wg.set_plot_scaler(length=L0, time=T0, mass=M0, dim=2) #to visualize dimensional results
    +frequency, response, axs = wg.compute_response(dof=dof, z=h/2, spectrum=excitation.spectrum, plot=True)
    +axs[0].get_lines()[0].set_color("black")
    +axs[0].set_xlim([0, 0.5e6])
    +axs[0].set_ylim([0, 1e-4])
    +plt.close()
    +
    +###############################################################################
    +# Time response\
    +# Results are to be compared with Fig. 8a of Treyssede and Laguerre, JASA 133 (2013), 3827-3837
    +response = Signal(frequency=frequency, spectrum=response, alpha=0*np.log(50)/5e-3*T0)
    +#response.plot_spectrum()
    +response.ifft(coeff=1)
    +fig, ax = plt.subplots(1,1)
    +fig.set_figheight(4)
    +fig.set_figwidth(10)
    +ax = response.plot(ax=ax)
    +ax.set_xlim([0, 5e-3])
    +ax.set_xlabel('time (s)')
    +ax.set_ylim([-2e-3, 2e-3])
    +ax.set_ylabel('displacement (m)')
    +#plt.savefig('plate_zgv_transient.png')
    +plt.show()
    +
    +##################################
    +# Mesh visualization
    +grid = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(mesh, mesh.topology.dim))
    +plotter = pyvista.Plotter()
    +plotter.add_mesh(grid, show_edges=True)
    +plotter.add_mesh(grid, style='points', render_points_as_spheres=True, point_size=10)
    +plotter.view_xy()
    +plotter.show()
    +
    +##################################
    +# Mode shape visualization
    +ik, imode = 100, 5 #parameter index, mode index to visualize
    +vec = wg.eigenvectors[ik].getColumnVector(imode)*wg.plot_scaler["eigenvectors"] #note: multiplying by wg.plot_scaler["eigenvectors"] enables to normalize the cross-section power flow of eigenmodes to 1 Watt(/m)
    +u_grid = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(V))
    +u_grid["u"] = np.array(vec).real.reshape(int(np.array(vec).size/V.element.value_shape), int(V.element.value_shape)) #V.element.value_shape is equal to 2
    +u_grid["u"] = np.insert(u_grid["u"], 1, 0, axis=1) #insert a zero column to the second component (the y component)
    +u_plotter = pyvista.Plotter()
    +u_plotter.add_mesh(grid, style="wireframe", color="k") #FE mesh
    +u_plotter.add_mesh(u_grid.warp_by_vector("u", factor=1e-2), opacity=0.8, show_scalar_bar=True, show_edges=False) #do not show edges of higher order elements with pyvista
    +u_plotter.view_zx()
    +u_plotter.show_axes()
    +u_plotter.show()
    +
    +###############################################################################
    +# Save matrices into file (for basic usage of waveguicsx, see README examples)
    +viewer = PETSc.Viewer().createBinary('BasicExample_K0K1K2MF.dat', 'w')
    +K0.view(viewer=viewer)
    +K1.view(viewer=viewer)
    +K2.view(viewer=viewer)
    +M.view(viewer=viewer)
    +F.view(viewer=viewer)
    +
    +
    +
    +
    +

    7. Dispersion curves of a rail

    +

    3D elastic waveguide example. +The cross-section is a 2D rail profile (60E1, 60.21kg/m) with free boundary conditions on its 1D boundaries, material: elastic steel. +This eigenproblem is solved with the varying parameter as the frequency (eigenvalues are then wavenumbers). +The FE mesh is built from gmsh with a .geo file.

    +
    # This file is a tutorial for waveguicsx (*), whose inputs are
    +# the matrices K0, K1, K2, M and the excitation vector F.
    +# In the tutorial, K0, K1, K2 and M are finite element matrices generated by FEnicSX (**).
    +#  (*) waveguicsx is a python library for solving complex waveguide problems
    +#      Copyright (C) 2023-2024  Fabien Treyssede
    +#      waveguicsx is free software distributed under the GNU General Public License
    +#      (https://github.com/treyssede/waveguicsx)
    +# (**) FEniCSx is an open-source computing platform for solving partial differential equations
    +#      distributed under the GNU Lesser General Public License (https://fenicsproject.org/)
    +
    +##################################
    +# 3D elastic waveguide example\
    +# The cross-section is a 2D rail profile (60E1, 60.21kg/m) with free boundary conditions on its 1D boundaries, material: elastic steel\
    +# The waveguide FE formulation (SAFE) leads to the following eigenvalue problem:\
    +# $(\textbf{K}_1-\omega^2\textbf{M}+\text{i}k(\textbf{K}_2+\textbf{K}_2^\text{T})+k^2\textbf{K}_3)\textbf{U}=\textbf{0}$\
    +# This eigenproblem is solved with the varying parameter as the frequency (eigenvalues are then wavenumbers).\
    +# The FE mesh is built from gmsh with a .geo file.
    +
    +import dolfinx
    +import ufl
    +from mpi4py import MPI
    +from petsc4py import PETSc
    +from slepc4py import SLEPc
    +import numpy as np
    +import matplotlib.pyplot as plt
    +import pyvista
    +
    +from waveguicsx.waveguide import Waveguide
    +#For proper use with a jupyter notebook, uncomment the following line:
    +#pyvista.set_jupyter_backend("none"); pyvista.start_xvfb() #try also: "static", "pythreejs", "ipyvtklink"...
    +
    +##################################
    +# Input parameters
    +a = np.sqrt(76.70e-4)/2 #characteristic length (m) = halfwidth of a square of identical cross-section (rail cross-section : 76.70cm2)
    +le = a/8 #finite element characteristic length (m)
    +rho, cs, cl = 7850.0, 3207.7, 6001.0 #density (kg/m3), shear and longitudinal wave celerities (m/s)
    +kappas, kappal = 0*0.008, 0*0.003 #shear and longitudinal bulk wave attenuations (Np/wavelength)
    +omega, nev = 2*np.pi*np.linspace(100, 5000, 50), 20 #angular frequencies (rad/s) and number of eigenvalues (low-frequency regime)
    +#wavenumber, nev = 2*np.pi*np.linspace(1e3, 1e5, 100)/cs, 100 #angular frequencies (rad/s) and number of eigenvalues (high-frequency regime)
    +#reminder:
    +#E, nu, rho = 2.1e+11, 0.3, 7850
    +#cs = np.sqrt(E/rho/(2*(1+nu)))
    +#cl = np.sqrt(E/rho*(1-nu)/((1+nu)*(1-2*nu)))
    +
    +##################################
    +# Re-scaling
    +L0 = a #characteristic length
    +T0 = a/cs #characteristic time
    +M0 = rho*a**3#characteristic mass
    +a, le = a/L0, le/L0
    +rho, cs, cl = rho/M0*L0**3, cs/L0*T0, cl/L0*T0
    +omega = omega*T0
    +#wavenumber = wavenumber*L0
    +cs, cl = cs/(1+1j*kappas/2/np.pi), cl/(1+1j*kappal/2/np.pi) #complex celerities
    +
    +##################################
    +# Build mesh from Gmsh and import to dolfinx (six-node triangles with three dofs per node for the three components of displacement)
    +
    +!gmsh rail60E1_60.21kg_m.geo -2 -order 2 -setnumber Mesh.MeshSizeFactor {le*L0*1000} -setnumber Mesh.ScalingFactor {1/(1000*L0)} #units in .geo are in mm
    +mesh, cell_tags, facet_tags = dolfinx.io.gmshio.read_from_msh("rail60E1_60.21kg_m.msh", MPI.COMM_WORLD, rank=0, gdim=2)
    +
    +##################################
    +# Visualize FE mesh with pyvista
    +Vmesh = dolfinx.fem.FunctionSpace(mesh, ufl.FiniteElement("CG", "triangle", 1)) #order 1 is properly handled with pyvista
    +plotter = pyvista.Plotter()
    +grid = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(Vmesh))
    +grid.cell_data["Marker"] = cell_tags.values
    +grid.set_active_scalars("Marker")
    +plotter.add_mesh(grid, show_edges=True, show_scalar_bar=False)
    +plotter.view_xy()
    +plotter.show()
    +# Finite element space
    +element = ufl.VectorElement("CG", "triangle", 2, 3) #Lagrange element, triangle, quadratic "P2", 3D vector
    +V = dolfinx.fem.FunctionSpace(mesh, element)
    +
    +##################################
    +# Create Material properties (isotropic)
    +def isotropic_law(rho, cs, cl):
    +    E, nu = rho*cs**2*(3*cl**2-4*cs**2)/(cl**2-cs**2), 0.5*(cl**2-2*cs**2)/(cl**2-cs**2)
    +    C11 = C22 = C33 = E/(1+nu)/(1-2*nu)*(1-nu)
    +    C12 = C13 = C23 = E/(1+nu)/(1-2*nu)*nu
    +    C44 = C55 = C66 = E/(1+nu)/2
    +    return np.array([[C11,C12,C13,0,0,0], 
    +                     [C12,C22,C23,0,0,0], 
    +                     [C13,C23,C33,0,0,0], 
    +                     [0,0,0,C44,0,0], 
    +                     [0,0,0,0,C55,0], 
    +                     [0,0,0,0,0,C66]])
    +C = isotropic_law(rho, cs, cl)
    +C = dolfinx.fem.Constant(mesh, PETSc.ScalarType(C))
    +
    +##################################
    +# Create free boundary conditions
    +bcs = []
    +
    +##################################
    +# Define variational problem (SAFE method)
    +u = ufl.TrialFunction(V)
    +v = ufl.TestFunction(V)
    +Lxy = lambda u: ufl.as_vector([u[0].dx(0), u[1].dx(1), 0, u[0].dx(1)+u[1].dx(0), u[2].dx(0), u[2].dx(1)])
    +Lz  = lambda u: ufl.as_vector([0, 0, u[2], 0, u[0], u[1]])
    +k0 = ufl.inner(C*Lxy(u), Lxy(v)) * ufl.dx
    +k0_form = dolfinx.fem.form(k0)
    +k1 = ufl.inner(C*Lz(u), Lxy(v)) * ufl.dx
    +k1_form = dolfinx.fem.form(k1)
    +k2 = ufl.inner(C*Lz(u), Lz(v)) * ufl.dx
    +k2_form = dolfinx.fem.form(k2)
    +m = rho*ufl.inner(u, v) * ufl.dx
    +mass_form = dolfinx.fem.form(m)
    +
    +##################################
    +# Build PETSc matrices
    +M = dolfinx.fem.petsc.assemble_matrix(mass_form, bcs=bcs, diagonal=0.0)
    +M.assemble()
    +K0 = dolfinx.fem.petsc.assemble_matrix(k0_form, bcs=bcs)
    +K0.assemble()
    +K1 = dolfinx.fem.petsc.assemble_matrix(k1_form, bcs=bcs, diagonal=0.0)
    +K1.assemble()
    +K2 = dolfinx.fem.petsc.assemble_matrix(k2_form, bcs=bcs, diagonal=0.0)
    +K2.assemble()
    +
    +##################################
    +# Solve the eigenproblem with SLEPc (the parameter is omega, the eigenvalue is k)
    +wg = Waveguide(MPI.COMM_WORLD, M, K0, K1, K2)
    +wg.set_parameters(omega=omega)
    +#wg.set_parameters(wavenumber=wavenumber)
    +wg.evp.setTolerances(tol=1e-8, max_it=20)
    +wg.solve(nev=nev, target=0) #access to components with: wg.eigenvalues[ik][imode], wg.eigenvectors[ik][idof,imode]
    +
    +##################################
    +# Plot dispersion curves in the low-frequency regime (for comparison, see e.g. Zhang et al., Mechanical Systems and Signal Processing, 150, 1-22 (2021))
    +wg.set_plot_scaler(length=L0, time=T0, mass=M0)
    +sc = wg.plot()
    +sc.axes.set_xlim([0, 25])
    +sc.axes.set_ylim([0, 5000])
    +sc = wg.plot_phase_velocity()
    +sc.axes.set_xlim([0, 5000])
    +sc.axes.set_ylim([0, 6000])
    +sc = wg.plot_group_velocity(direction=+1)
    +sc.axes.set_xlim([0, 5000])
    +sc.axes.set_ylim([0, 6000])
    +plt.show()
    +
    +###############################################################################
    +# Plot phase velocity dispersion curves in the high-frequency regime (for comparison, see e.g. Ge et al., Structural Health Monitoring, 21, 1287-1308 (2022))
    +if False:
    +    wg.set_plot_scaler(length=L0, time=T0, mass=M0)
    +    wg.plot_scaler['frequency'] = wg.plot_scaler['frequency']/1000
    +    sc = wg.plot_phase_velocity()
    +    sc.axes.set_xlim([0, 1e2])
    +    sc.axes.set_ylim([0, 12000])
    +    sc.axes.set_xlabel('frequency (kHz)')
    +    sc.axes.set_ylabel('phase velocity (m/s)')
    +    #plt.savefig('rail_phase_velocity.png')
    +    plt.show()
    +
    +##################################
    +# Mode shape visualization
    +ik, imode = 49, 9 #parameter index, mode index to visualize
    +print(f"f={wg.omega[ik]/(2*np.pi*T0):1.1f}Hz, l={2*np.pi*L0/wg.eigenvalues[ik][imode].real:1.2f}m")
    +vec = wg.eigenvectors[ik].getColumnVector(imode)
    +grid_interp = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(Vmesh))
    +grid = pyvista.UnstructuredGrid(*dolfinx.plot.create_vtk_mesh(V))
    +grid["u"] = np.array(vec).real.reshape(int(np.array(vec).size/V.element.value_shape), int(V.element.value_shape)) #V.element.value_shape is equal to 3
    +grid_interp = grid_interp.interpolate(grid) #interpolation onto a finite element mesh of order 1 to plot both the FE mesh and the results with pyvista
    +plotter = pyvista.Plotter()
    +plotter.add_mesh(grid_interp.warp_by_vector("u", factor=0.1), show_scalar_bar=False, show_edges=True)
    +#plotter.show_axes()
    +plotter.view_xy()
    +plotter.screenshot('rail_mode_shape.png', transparent_background=True)
    +plotter.show()
    +
    +
    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/waveguicsx.html b/docs/waveguicsx.html index 0124629..5902204 100644 --- a/docs/waveguicsx.html +++ b/docs/waveguicsx.html @@ -60,14 +60,14 @@
  • Tutorials