# Generalized Explicit Runge-Kutta (GERK)
A package for the curious mathematicians and engineers who want to experiment the Runge-Kutta method with their own coefficients.
[PyPI link](https://pypi.org/project/gerk/)
[GitHub link](https://github.com/yfnaji/Gerk)
## Motivation
I decided to create this package to give mathematicians or engineers the freedom to create and use the Runge-Kutta method of their choice,
as opposed to be being locked in to whatever a package provides you.
## Runge-Kutta Overview
Runge-Kutta methods aim to numerically solve ordinary differential equations of the form:
$$
\frac{\text{d}y}{\text{d}x}=f(x,y)
$$
with a given initial condition $(x_0, y_0)$.
We define a member of the Runge-Kutta family with a *Butcher tableau*:
<img width="247" alt="genrk" src="https://github.com/yfnaji/Gerk/assets/59436765/c35b7df0-0080-41bf-8f1d-04a53ad9dac5">
The above tableau is often abbreviated to
<img width="103" alt="abbrk" src="https://github.com/yfnaji/Gerk/assets/59436765/561bd4fb-d488-44cf-9246-56cb392ad66d">
The Butcher tableau presents the categories of coefficients that will be used in our Runge-Kutta method.
The $n^\text{th}$ evaluation of the solution will be denoted as $(x_n, y_n)$. We also define $h$ as the time step i.e. the step size from the previous approximation to the next, and therefore
$$
x_{n+1} = x_{n} + h
$$
Before defining $y_n$, we must familiarize ourselves with the array $k$. We define the $i^{\text{th}}$ row of $k$ at $(x_n, y_n)$ as:
$$
k_i(x_n, y_n) = f\left(x_n + c_i h, y_n + \sum_{j=1}^{i-1} a_{ij}k_j(x_n, y_n)\right)
$$
where $f$ is the function defined in the differential equation above. Note the recursion in the second argument of $f$ where we sum rows preceding $k_{i}$ and apply a scale factor of $a_{ij}$.
We now have everything we need to calculate $y_{n+1}$:
$$
y_{n+1} = y_{n} + h \sum_{i=1}^{r}b_i k_{i}(x_n, y_n)
$$
The idea is to calculate various slopes at point $y_n$ to ascertain a weighted of the ascent (or descent) and add it to the previous approximation.
## What is Gerk?
Most packages for the Runge-Kutta method usually have the coefficients $a_{ij}$, $b_i$ and $c_i$ determined beforehand for known methods such as the *Forward-Euler method*, the *1/4 rule*, *the 3/8 rule* etc, but do not allow one to customerize their own Runge-Kutta.
Gerk is an easy interface to allow the user to determine their own coefficient values for the Runge-Kutta method.
## How to use Gerk
We can simply import `Gerk` in the following way:
```
from gerk import gerk
```
## Parameters
As seen in mathematics above, there is quite a bit information required to execute the Runge-Kutta method. This has been broken down into arguments to be passed into the `Gerk` class:
- `a` The $A$ matrix in the Butcher tableau. This **must** be a lower triangular matrix that is formatted as a list of lists that contain floats, or integers
- `b` The $b$ array. Must be a list of floats, decimals or integers
- `c` The $c$ array. Must be a list of floats, decimals or integers
- `initial` A `tuple` that acts as the coordinate of the initial condition values $(x_0, y_0)$
- `terminal` The value of $x$ for which we terminate the Runge-Kutta method
- `timesteps` The number of times steps you want to apply on the from the starting point $x_0$ to `final`. Must be an integer
- `func` The function expression to be numerically integrated
- `enforce_rules` (Optional) A boolean to enforce conventional Runge-Kutta rules
The function will output the time steps and approximated y values as a tuple of lists of floats.
## Conditions
There is no consensus to what conditions must hold regarding the coefficients you choose for your method, however, some known Runge-Kutta methods do consistently conform to some known conditions.
These conditions are
$$\sum_{i=1}^{r}b_i=1 \ \ \ \ \sum_{i=1}^{r}b_ic_i = 1/2 \ \ \ \ \sum_{j=1}^{r}a_{ij} = c_i$$
which can be enforced by the `enforce_rules` parameter, which is defaulted to `False`.
## Example
Let us numerically solve the following initial value problem:
$$
\frac{\text{d}y}{\text{d}x} = y
$$
with the initial value $(0, 1)$. We want to apply a Runge-Kutta method with the following Butcher tableau:
<img width="236" src="https://github.com/yfnaji/Gerk/assets/59436765/0105ab38-e0b3-4b97-b5d7-d3fcc1eddb2b">
The $A$ lower triangular matrix in the Butcher tableau above can be implemented in the following way:
```python
a = [
[1/3],
[-1/3, 1],
[1, -1, 1]
]
```
Now for the `b` and `c` vectors:
```python
b = [1/8, 3/8, 3/8, 1/8]
c = [0, 1/3, 2/3, 1]
```
Finally, we can define the function with a lambda function:
```
func = lambda x, y: y
```
Now we are ready to run `gerk()` and plot its output:
```python
import matplotlib.pyplot as plt
from gerk import gerk
x, y = gerk(
a=a,
b=b,
c=c,
initial=(0, 1),
timesteps=10000,
terminal=1,
func=lambda x, y: y,
)
plt.plot(x, y, color="r")
plt.show()
```
The code above yields the following graph:
<img width="500" alt="gerk_1" src=https://github.com/user-attachments/assets/b17a9a2b-8286-464a-a0aa-9a72f6f0a9d2>
## Adaptive Runge-Kutta Methods
There is an alternate way to utilise the Runge-Kutta method by employing an additional distinct $b$ array. The Butcher tableau for such methods take the form:
<img width="337" alt="adaptive_butcher" src="https://github.com/user-attachments/assets/855c531c-a75c-485b-9f30-2f6b2a379abe">
where $b_1$ and $b_2$ are the two distinct $b$ arrays.
This method is not too disimilar to the original Runge-Kutta method. We calculate the $k$'s in the same way as before, but there is an extra step when evaluating $y_{n+1}$.
Although we do calculate $y_{n+1}$ in same way outlined above, we also calculate $\gamma_{n+1}$:
$$
y_{n+1} = y_{n} + h \sum_{i=1}^{r}b_{1i}\cdot k_{i}(x_n, y_n) \ \ \ \ \ \ \ \ \ \gamma_{n+1} = y_{n} + h \sum_{i=1}^{r} b_{2i}\cdot k_{i}(x_n, y_n)
$$
_Note_ that the calculation for $\gamma$ requires the use of $y_n$ and **not** $\gamma_n$.
For every time step, we calculate the following error:
$$
E := \left|y_{n+1}-\gamma_{n+1}\right|
$$
Now we define the tolerance, $\mathcal{E}$, which will act as the maximum acceptable value for $E$.
If $E<\mathcal{E}$, then we accept the value of $y_{n+1}$ and we increment $x_{n}$ by $h$ and start the process again for $x_{n+1}$ and $y_{n+1}$ as normal.
However, if $E\geq\mathcal{E}$, we will need to adjust the value of $h$ and redo the calculation with this renewed time step value in an attempt to satisfy the condition $E<\mathcal{E}$.
The value of $h$ will be adjusted as follows:
$$
h \rightarrow 0.9\cdot h \cdot\sqrt[n]{\frac{h}{\mathcal{E}}}
$$
where $n=\min\left(p,q\right)$, where $p$ and $q$ are the orders $^1$ of $b_i$ and $b^*_i$ respectively.
$^1$ A Runge-Kutta method with matrix $a_{ij}$ and arrays $b$ and $c$ has order $p$ if
$$
\sum_{i=1}^{p}b_i = 1 \ \ \ \ \sum_{i=1}^{p}b_ic_i = 1/2 \ \ \ \ \sum_{j=1}^{p}a_{ij} = c_i \ \ \ \
$$
## Adaptive Runge-Kutta example
In this example, we will try to numerically integrate
$$
\frac{\text{d}y}{\text{d}x}=-2xy
$$
with initial conditions $(-1, e^{-1})$. Note that the exact solution is $y=e^{-x^2}$.
Here we will use the Bogacki–Shampine (BS23) method which has the following Butcher tableau:
<img width="247" alt="adapt_butcher" src="https://github.com/user-attachments/assets/95a88663-cfce-4654-ae14-5fcff6057da2">
For the adaptive Runge-Kutta method, we will use `adaptive_gerk()`. This method's paramters vary slightly from `gerk()`:
- `a` The $A$ matrix in the Butcher tableau. This **must** be a lower triangular matrix that is formatted as a list of lists that contain floats, or integers
- `b_1` The $b_1$ array. Must be a list of floats, decimals or integers
- `b_2` The $b_2$ array. Must be a list of floats, decimals or integers
- `c` The $c$ array. Must be a list of floats, decimals or integers
- `initial` A `tuple` that acts as the coordinate of the initial condition values $(x_0, y_0)$
- `terminal` The value of $x$ for which we terminate the Runge-Kutta method
- `timesteps` The number of times steps you want to apply on the from the starting point $x_0$ to `final`. Must be an integer
- `func` The function expression to be numerically integrated
- `enforce_rules` (Optional) A boolean to enforce conventional Runge-Kutta rules. Defaulted to `False`
- `tolerance` (Optional) A float representing the maximum threshold of the adaptive step. Defaulted to `1e-4`
Similar to `gerk()`, this function will output the time steps and approximated y values as a tuple of lists of floats.
```python
from math import exp
import matplotlib.pyplot as plt
from gerk import adaptive_gerk
a = [
[1/2],
[0, 3/4],
[2/9, 1/3, 4/9]
]
b_1 = [2/9, 1/3, 4/9, 0]
b_2 = [7/24, 1/4, 1/3, 1/8]
c = [0, 1/2, 3/4, 1]
x, y = adaptive_gerk(
a=a,
b_1=b_1,
b_2=b_2,
c=c,
initial=(-1, exp(-1)),
timesteps=10000,
terminal=1,
func=lambda x, y: -2 * x * y,
)
plt.plot(x, y, color="r")
plt.show()
```
The code above yields the following graph:
<img width="500" alt="gerk_2" src=https://github.com/user-attachments/assets/a472aafa-b096-444c-b0b7-86afb988a3c2>
## In the pipeline
* To further generalise the Runge-Kutta method with an indefinite number of $b$ and $c$ arrays
* Implement *Cython* for faster execution times
* More descriptive error messages
* Add unit tests
* Implement Stochastic Runge-Kutta method
Raw data
{
"_id": null,
"home_page": null,
"name": "gerk",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "runge-kutta, runge, kutta, numerical, integration, approximation",
"author": "Yasser Naji",
"author_email": "yfnaji@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/cc/b9/8b2aca607028c252fb2b1f604a2b8f6fc9aea3f89de25f623686574b54b9/gerk-1.0.2.tar.gz",
"platform": null,
"description": "# Generalized Explicit Runge-Kutta (GERK)\n\nA package for the curious mathematicians and engineers who want to experiment the Runge-Kutta method with their own coefficients.\n\n[PyPI link](https://pypi.org/project/gerk/)\n\n[GitHub link](https://github.com/yfnaji/Gerk)\n\n## Motivation\n\nI decided to create this package to give mathematicians or engineers the freedom to create and use the Runge-Kutta method of their choice,\nas opposed to be being locked in to whatever a package provides you.\n\n## Runge-Kutta Overview\n\nRunge-Kutta methods aim to numerically solve ordinary differential equations of the form:\n\n$$\n\\frac{\\text{d}y}{\\text{d}x}=f(x,y)\n$$\n\nwith a given initial condition $(x_0, y_0)$. \n\nWe define a member of the Runge-Kutta family with a *Butcher tableau*:\n\n<img width=\"247\" alt=\"genrk\" src=\"https://github.com/yfnaji/Gerk/assets/59436765/c35b7df0-0080-41bf-8f1d-04a53ad9dac5\">\n\nThe above tableau is often abbreviated to\n\n<img width=\"103\" alt=\"abbrk\" src=\"https://github.com/yfnaji/Gerk/assets/59436765/561bd4fb-d488-44cf-9246-56cb392ad66d\">\n\nThe Butcher tableau presents the categories of coefficients that will be used in our Runge-Kutta method.\n\nThe $n^\\text{th}$ evaluation of the solution will be denoted as $(x_n, y_n)$. We also define $h$ as the time step i.e. the step size from the previous approximation to the next, and therefore\n\n$$\nx_{n+1} = x_{n} + h\n$$\n\nBefore defining $y_n$, we must familiarize ourselves with the array $k$. We define the $i^{\\text{th}}$ row of $k$ at $(x_n, y_n)$ as:\n\n$$\nk_i(x_n, y_n) = f\\left(x_n + c_i h, y_n + \\sum_{j=1}^{i-1} a_{ij}k_j(x_n, y_n)\\right)\n$$\n\nwhere $f$ is the function defined in the differential equation above. Note the recursion in the second argument of $f$ where we sum rows preceding $k_{i}$ and apply a scale factor of $a_{ij}$.\n\nWe now have everything we need to calculate $y_{n+1}$:\n\n$$\ny_{n+1} = y_{n} + h \\sum_{i=1}^{r}b_i k_{i}(x_n, y_n)\n$$\n\nThe idea is to calculate various slopes at point $y_n$ to ascertain a weighted of the ascent (or descent) and add it to the previous approximation.\n\n## What is Gerk?\n\nMost packages for the Runge-Kutta method usually have the coefficients $a_{ij}$, $b_i$ and $c_i$ determined beforehand for known methods such as the *Forward-Euler method*, the *1/4 rule*, *the 3/8 rule* etc, but do not allow one to customerize their own Runge-Kutta.\n\nGerk is an easy interface to allow the user to determine their own coefficient values for the Runge-Kutta method.\n\n## How to use Gerk\n\nWe can simply import `Gerk` in the following way:\n\n```\nfrom gerk import gerk\n```\n\n## Parameters\n\nAs seen in mathematics above, there is quite a bit information required to execute the Runge-Kutta method. This has been broken down into arguments to be passed into the `Gerk` class:\n\n- `a` The $A$ matrix in the Butcher tableau. This **must** be a lower triangular matrix that is formatted as a list of lists that contain floats, or integers\n- `b` The $b$ array. Must be a list of floats, decimals or integers\n- `c` The $c$ array. Must be a list of floats, decimals or integers\n- `initial` A `tuple` that acts as the coordinate of the initial condition values $(x_0, y_0)$\n- `terminal` The value of $x$ for which we terminate the Runge-Kutta method\n- `timesteps` The number of times steps you want to apply on the from the starting point $x_0$ to `final`. Must be an integer\n- `func` The function expression to be numerically integrated\n- `enforce_rules` (Optional) A boolean to enforce conventional Runge-Kutta rules\n\nThe function will output the time steps and approximated y values as a tuple of lists of floats.\n\n## Conditions\n\nThere is no consensus to what conditions must hold regarding the coefficients you choose for your method, however, some known Runge-Kutta methods do consistently conform to some known conditions.\n\nThese conditions are\n\n$$\\sum_{i=1}^{r}b_i=1 \\ \\ \\ \\ \\sum_{i=1}^{r}b_ic_i = 1/2 \\ \\ \\ \\ \\sum_{j=1}^{r}a_{ij} = c_i$$\n\nwhich can be enforced by the `enforce_rules` parameter, which is defaulted to `False`.\n\n## Example\n\nLet us numerically solve the following initial value problem:\n\n$$\n\\frac{\\text{d}y}{\\text{d}x} = y\n$$\n\nwith the initial value $(0, 1)$. We want to apply a Runge-Kutta method with the following Butcher tableau:\n\n<img width=\"236\" src=\"https://github.com/yfnaji/Gerk/assets/59436765/0105ab38-e0b3-4b97-b5d7-d3fcc1eddb2b\">\n\nThe $A$ lower triangular matrix in the Butcher tableau above can be implemented in the following way:\n\n```python\na = [\n [1/3],\n [-1/3, 1],\n [1, -1, 1]\n]\n```\n\nNow for the `b` and `c` vectors:\n\n```python\nb = [1/8, 3/8, 3/8, 1/8]\n\nc = [0, 1/3, 2/3, 1]\n```\n\nFinally, we can define the function with a lambda function:\n\n```\nfunc = lambda x, y: y\n```\n\nNow we are ready to run `gerk()` and plot its output:\n\n```python\nimport matplotlib.pyplot as plt\nfrom gerk import gerk\n\nx, y = gerk(\n a=a, \n b=b, \n c=c, \n initial=(0, 1), \n timesteps=10000, \n terminal=1,\n func=lambda x, y: y,\n )\n\nplt.plot(x, y, color=\"r\")\nplt.show()\n```\n\nThe code above yields the following graph:\n\n<img width=\"500\" alt=\"gerk_1\" src=https://github.com/user-attachments/assets/b17a9a2b-8286-464a-a0aa-9a72f6f0a9d2>\n\n## Adaptive Runge-Kutta Methods\n\nThere is an alternate way to utilise the Runge-Kutta method by employing an additional distinct $b$ array. The Butcher tableau for such methods take the form:\n\n<img width=\"337\" alt=\"adaptive_butcher\" src=\"https://github.com/user-attachments/assets/855c531c-a75c-485b-9f30-2f6b2a379abe\">\n\nwhere $b_1$ and $b_2$ are the two distinct $b$ arrays.\n\nThis method is not too disimilar to the original Runge-Kutta method. We calculate the $k$'s in the same way as before, but there is an extra step when evaluating $y_{n+1}$. \n\nAlthough we do calculate $y_{n+1}$ in same way outlined above, we also calculate $\\gamma_{n+1}$:\n\n$$\ny_{n+1} = y_{n} + h \\sum_{i=1}^{r}b_{1i}\\cdot k_{i}(x_n, y_n) \\ \\ \\ \\ \\ \\ \\ \\ \\ \\gamma_{n+1} = y_{n} + h \\sum_{i=1}^{r} b_{2i}\\cdot k_{i}(x_n, y_n)\n$$\n\n_Note_ that the calculation for $\\gamma$ requires the use of $y_n$ and **not** $\\gamma_n$.\n\nFor every time step, we calculate the following error:\n\n$$\nE := \\left|y_{n+1}-\\gamma_{n+1}\\right|\n$$\n\nNow we define the tolerance, $\\mathcal{E}$, which will act as the maximum acceptable value for $E$.\n\nIf $E<\\mathcal{E}$, then we accept the value of $y_{n+1}$ and we increment $x_{n}$ by $h$ and start the process again for $x_{n+1}$ and $y_{n+1}$ as normal. \n\nHowever, if $E\\geq\\mathcal{E}$, we will need to adjust the value of $h$ and redo the calculation with this renewed time step value in an attempt to satisfy the condition $E<\\mathcal{E}$. \n\nThe value of $h$ will be adjusted as follows:\n\n$$\nh \\rightarrow 0.9\\cdot h \\cdot\\sqrt[n]{\\frac{h}{\\mathcal{E}}}\n$$\n\nwhere $n=\\min\\left(p,q\\right)$, where $p$ and $q$ are the orders $^1$ of $b_i$ and $b^*_i$ respectively.\n\n$^1$ A Runge-Kutta method with matrix $a_{ij}$ and arrays $b$ and $c$ has order $p$ if\n\n$$\n\\sum_{i=1}^{p}b_i = 1 \\ \\ \\ \\ \\sum_{i=1}^{p}b_ic_i = 1/2 \\ \\ \\ \\ \\sum_{j=1}^{p}a_{ij} = c_i \\ \\ \\ \\ \n$$\n\n## Adaptive Runge-Kutta example\n\nIn this example, we will try to numerically integrate\n\n$$\n\\frac{\\text{d}y}{\\text{d}x}=-2xy\n$$\n\nwith initial conditions $(-1, e^{-1})$. Note that the exact solution is $y=e^{-x^2}$.\n\nHere we will use the Bogacki\u2013Shampine (BS23) method which has the following Butcher tableau:\n\n<img width=\"247\" alt=\"adapt_butcher\" src=\"https://github.com/user-attachments/assets/95a88663-cfce-4654-ae14-5fcff6057da2\">\n\nFor the adaptive Runge-Kutta method, we will use `adaptive_gerk()`. This method's paramters vary slightly from `gerk()`:\n\n- `a` The $A$ matrix in the Butcher tableau. This **must** be a lower triangular matrix that is formatted as a list of lists that contain floats, or integers\n- `b_1` The $b_\uff11$ array. Must be a list of floats, decimals or integers\n- `b_2` The $b_2$ array. Must be a list of floats, decimals or integers\n- `c` The $c$ array. Must be a list of floats, decimals or integers\n- `initial` A `tuple` that acts as the coordinate of the initial condition values $(x_0, y_0)$\n- `terminal` The value of $x$ for which we terminate the Runge-Kutta method\n- `timesteps` The number of times steps you want to apply on the from the starting point $x_0$ to `final`. Must be an integer\n- `func` The function expression to be numerically integrated\n- `enforce_rules` (Optional) A boolean to enforce conventional Runge-Kutta rules. Defaulted to `False`\n- `tolerance` (Optional) A float representing the maximum threshold of the adaptive step. Defaulted to `1e-4`\n\nSimilar to `gerk()`, this function will output the time steps and approximated y values as a tuple of lists of floats.\n\n```python\nfrom math import exp\nimport matplotlib.pyplot as plt\nfrom gerk import adaptive_gerk\n\na = [\n [1/2],\n [0, 3/4],\n [2/9, 1/3, 4/9]\n]\nb_1 = [2/9, 1/3, 4/9, 0]\nb_2 = [7/24, 1/4, 1/3, 1/8]\nc = [0, 1/2, 3/4, 1]\n\nx, y = adaptive_gerk(\n a=a, \n b_1=b_1, \n b_2=b_2, \n c=c, \n initial=(-1, exp(-1)), \n timesteps=10000, \n terminal=1,\n func=lambda x, y: -2 * x * y,\n )\n\nplt.plot(x, y, color=\"r\")\nplt.show()\n```\n\nThe code above yields the following graph:\n\n<img width=\"500\" alt=\"gerk_2\" src=https://github.com/user-attachments/assets/a472aafa-b096-444c-b0b7-86afb988a3c2>\n\n## In the pipeline\n\n* To further generalise the Runge-Kutta method with an indefinite number of $b$ and $c$ arrays\n* Implement *Cython* for faster execution times\n* More descriptive error messages\n* Add unit tests\n* Implement Stochastic Runge-Kutta method\n",
"bugtrack_url": null,
"license": null,
"summary": "Generalized Explicit Runge-Kutta",
"version": "1.0.2",
"project_urls": null,
"split_keywords": [
"runge-kutta",
" runge",
" kutta",
" numerical",
" integration",
" approximation"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "dd4754e63ce147509fac4d7cabe0f923a8caf68f910184343e47d60a5e31a74f",
"md5": "7dd9abe12194386c8d619c4dbd8a965e",
"sha256": "08f881af5a9ceefe51d07b8dace8a316cfcfed6c5d28ab41342e99e4f6c22e3d"
},
"downloads": -1,
"filename": "gerk-1.0.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "7dd9abe12194386c8d619c4dbd8a965e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 7631,
"upload_time": "2024-11-14T15:30:35",
"upload_time_iso_8601": "2024-11-14T15:30:35.127609Z",
"url": "https://files.pythonhosted.org/packages/dd/47/54e63ce147509fac4d7cabe0f923a8caf68f910184343e47d60a5e31a74f/gerk-1.0.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "ccb98b2aca607028c252fb2b1f604a2b8f6fc9aea3f89de25f623686574b54b9",
"md5": "8bf06a33fab9d48725fb35fd6ee66486",
"sha256": "048249fd7fc17d78187d558ec3c5bbf97ecb7508f203ac0969e000bb8978a667"
},
"downloads": -1,
"filename": "gerk-1.0.2.tar.gz",
"has_sig": false,
"md5_digest": "8bf06a33fab9d48725fb35fd6ee66486",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 6809,
"upload_time": "2024-11-14T15:30:36",
"upload_time_iso_8601": "2024-11-14T15:30:36.921533Z",
"url": "https://files.pythonhosted.org/packages/cc/b9/8b2aca607028c252fb2b1f604a2b8f6fc9aea3f89de25f623686574b54b9/gerk-1.0.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-11-14 15:30:36",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "gerk"
}