{"id":19811743,"url":"https://github.com/finsberg/fenicsx-beat","last_synced_at":"2025-09-18T05:32:16.553Z","repository":{"id":254003394,"uuid":"784117904","full_name":"finsberg/fenicsx-beat","owner":"finsberg","description":null,"archived":false,"fork":false,"pushed_at":"2024-12-18T18:12:06.000Z","size":9643,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-18T19:26:00.331Z","etag":null,"topics":["cardiac","ecg","electrophysiology","fenicsx","finite-element-analysis","monodomain-model"],"latest_commit_sha":null,"homepage":"https://finsberg.github.io/fenicsx-beat/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/finsberg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-09T08:12:48.000Z","updated_at":"2024-12-06T08:55:10.000Z","dependencies_parsed_at":"2024-08-20T21:16:38.407Z","dependency_job_id":"c720b282-4448-4320-bd3a-47eadc493eb6","html_url":"https://github.com/finsberg/fenicsx-beat","commit_stats":null,"previous_names":["finsberg/fenicsx-beat"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/finsberg%2Ffenicsx-beat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/finsberg%2Ffenicsx-beat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/finsberg%2Ffenicsx-beat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/finsberg%2Ffenicsx-beat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/finsberg","download_url":"https://codeload.github.com/finsberg/fenicsx-beat/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233451303,"owners_count":18678211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cardiac","ecg","electrophysiology","fenicsx","finite-element-analysis","monodomain-model"],"created_at":"2024-11-12T09:27:39.757Z","updated_at":"2025-09-18T05:32:16.536Z","avatar_url":"https://github.com/finsberg.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"![_](https://raw.githubusercontent.com/finsberg/fenicsx-beat/refs/heads/main/docs/_static/logo.png)\n\n# fenicsx-beat\nCardiac electrophysiology research uses computational modeling to study heart rhythm disorders and test therapies.\n\nThis tool, fenicsx-beat, is a cardiac electrophysiology simulator built specifically for the FEniCSx platform. It provides a dedicated and easy-to-use tool for researchers already using [FEniCSx](https://fenicsproject.org) to perform simulations based on the Monodomain model.\n\n- Source code: https://github.com/finsberg/fenicsx-beat\n- Documentation: https://finsberg.github.io/fenicsx-beat\n\n\n## Install\nYou can install the library with `pip`\n```\npython3 -m pip install fenicsx-beat\n```\nor with `conda`\n```\nconda install -c conda-forge fenicsx-beat\n```\nNote that installing with `pip` requires [FEniCSx already installed](https://fenicsproject.org/download/)\n\nAlso that to run most of the examples you will need to install additional dependencies which can be done using the command\n```\npython3 -m pip install fenicsx-beat[demos]\n```\n\n\n## Getting started\n\nThe following minimal example demonstrates simulating the Monodomain model on a unit square domain using a modified FitzHugh-Nagumo model\n\n```python\nimport shutil\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nfrom mpi4py import MPI\nimport dolfinx\nimport ufl\n\nimport beat\n\n# MPI communicator\ncomm = MPI.COMM_WORLD\n# Create mesh\nmesh = dolfinx.mesh.create_unit_square(comm, 32, 32, dolfinx.cpp.mesh.CellType.triangle)\n# Create a variable for time\ntime = dolfinx.fem.Constant(mesh, dolfinx.default_scalar_type(0.0))\n\n\n# Define forward euler scheme for solving the ODEs\n# This just needs to be a function that takes the current time, states, parameters and dt\n# and returns the new states\ndef fitzhughnagumo_forward_euler(t, states, parameters, dt):\n    s, v = states\n    (\n        c_1,\n        c_2,\n        c_3,\n        a,\n        b,\n        v_amp,\n        v_rest,\n        v_peak,\n        stim_amplitude,\n        stim_duration,\n        stim_start,\n    ) = parameters\n    i_app = np.where(\n        np.logical_and(t \u003e stim_start, t \u003c stim_start + stim_duration),\n        stim_amplitude,\n        0,\n    )\n    values = np.zeros_like(states)\n\n    ds_dt = b * (-c_3 * s + (v - v_rest))\n    values[0] = ds_dt * dt + s\n\n    v_th = v_amp * a + v_rest\n    I = -s * (c_2 / v_amp) * (v - v_rest) + (\n        ((c_1 / v_amp**2) * (v - v_rest)) * (v - v_th)\n    ) * (-v + v_peak)\n    dV_dt = I + i_app\n    values[1] = v + dV_dt * dt\n    return values\n\n\n# Define space for the ODEs\node_space = dolfinx.fem.functionspace(mesh, (\"P\", 1))\n\n# Define parameters for the ODEs\na = 0.13\nb = 0.013\nc1 = 0.26\nc2 = 0.1\nc3 = 1.0\nv_peak = 40.0\nv_rest = -85.0\nstim_amplitude = 100.0\nstim_duration = 1\nstim_start = 0.0\n\n# Collect the parameter in a numpy array\nparameters = np.array(\n    [\n        c1,\n        c2,\n        c3,\n        a,\n        b,\n        v_peak - v_rest,\n        v_rest,\n        v_peak,\n        stim_amplitude,\n        stim_duration,\n        stim_start,\n    ],\n    dtype=np.float64,\n)\n\n# Define the initial states\ninit_states = np.array([0.0, -85], dtype=np.float64)\n# Specify the index of state for the membrane potential\n# which will also inform the PDE solver later\nv_index = 1\n\n# We can also check the solution of the ODE\n# by solving the ODE for a single cell\ntimes = np.arange(0.0, 1000.0, 0.1)\nvalues = np.zeros((len(times), 2))\nvalues[0, :] = np.array([0.0, -85.0])\nfor i, t in enumerate(times[1:]):\n    values[i + 1, :] = fitzhughnagumo_forward_euler(t, values[i, :], parameters, dt=0.1)\n\n\nfig, ax = plt.subplots()\nax.plot(times, values[:, v_index])\nax.set_xlabel(\"Time\")\nax.set_ylabel(\"States\")\nax.legend()\nfig.savefig(\"ode_solution.png\")\n\n\n# Now we set external stimulus to zero for ODE\nparameters[-3] = 0.0\n\n# and create stimulus for PDE\nstim_expr = ufl.conditional(ufl.And(ufl.ge(time, 0.0), ufl.le(time, 0.5)), 600.0, 0.0)\nstim_marker = 1\ncells = dolfinx.mesh.locate_entities(\n    mesh, mesh.topology.dim, lambda x: np.logical_and(x[0] \u003c= 0.5, x[1] \u003c= 0.5)\n)\nstim_tags = dolfinx.mesh.meshtags(\n    mesh,\n    mesh.topology.dim,\n    cells,\n    np.full(len(cells), stim_marker, dtype=np.int32),\n)\ndx = ufl.Measure(\"dx\", domain=mesh, subdomain_data=stim_tags)\nI_s = beat.Stimulus(expr=stim_expr, dZ=dx, marker=stim_marker)\n\n# Create PDE model\npde = beat.MonodomainModel(time=time, mesh=mesh, M=0.001, I_s=I_s, dx=dx)\n\n# Next we create the PDE solver where we make sure to\n# pass the variable for the membrane potential from the PDE\node = beat.odesolver.DolfinODESolver(\n    v_ode=dolfinx.fem.Function(ode_space),\n    v_pde=pde.state,\n    fun=fitzhughnagumo_forward_euler,\n    init_states=init_states,\n    parameters=parameters,\n    num_states=len(init_states),\n    v_index=1,\n)\n\n# Combine PDE and ODE solver\nsolver = beat.MonodomainSplittingSolver(pde=pde, ode=ode)\n\n# Now we setup file for saving results\n# First remove any existing files\nshutil.rmtree(\"voltage.bp\", ignore_errors=True)\n\nvtx = dolfinx.io.VTXWriter(mesh.comm, \"voltage.bp\", [pde.state], engine=\"BP5\")\nvtx.write(0.0)\n\n# Finally we run the simulation for 400 ms using a time step of 0.01 ms\nT = 400.0\nt = 0.0\ndt = 0.01\ni = 0\nwhile t \u003c T:\n    v = solver.pde.state.x.array\n    solver.step((t, t + dt))\n    t += dt\n    if i % 500 == 0:\n        vtx.write(t)\n    i += 1\n\nvtx.close()\n\n```\n![_](https://raw.githubusercontent.com/finsberg/fenicsx-beat/refs/heads/main/docs/_static/simple.gif)\n![_](https://raw.githubusercontent.com/finsberg/fenicsx-beat/refs/heads/main/joss-paper/paper_figure.png)\n\nSee more examples in the [documentation](https://finsberg.github.io/fenicsx-beat)\n\n## License\nMIT\n\n## Need help or having issues\nPlease submit an [issue](https://github.com/finsberg/fenicsx-beat/issues)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffinsberg%2Ffenicsx-beat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffinsberg%2Ffenicsx-beat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffinsberg%2Ffenicsx-beat/lists"}