OGRePy 1.2.0__tar.gz → 1.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ogrepy-1.2.0 → ogrepy-1.3.1}/LICENSE.txt +1 -1
- {ogrepy-1.2.0 → ogrepy-1.3.1}/OGRePy/__init__.py +12 -7
- {ogrepy-1.2.0 → ogrepy-1.3.1}/OGRePy/_core.py +93 -57
- {ogrepy-1.2.0 → ogrepy-1.3.1}/OGRePy/abc.py +11 -6
- {ogrepy-1.2.0 → ogrepy-1.3.1}/OGRePy/docs/OGRePy_Documentation.html +398 -379
- {ogrepy-1.2.0 → ogrepy-1.3.1}/OGRePy/docs/OGRePy_Documentation.ipynb +1123 -1101
- ogrepy-1.3.1/OGRePy/docs/OGRePy_Documentation.pdf +0 -0
- {ogrepy-1.2.0 → ogrepy-1.3.1}/OGRePy.egg-info/PKG-INFO +33 -13
- {ogrepy-1.2.0 → ogrepy-1.3.1}/PKG-INFO +33 -13
- {ogrepy-1.2.0 → ogrepy-1.3.1}/README.md +29 -10
- {ogrepy-1.2.0 → ogrepy-1.3.1}/pyproject.toml +6 -4
- ogrepy-1.2.0/OGRePy/docs/OGRePy_Documentation.pdf +0 -0
- {ogrepy-1.2.0 → ogrepy-1.3.1}/OGRePy/py.typed +0 -0
- {ogrepy-1.2.0 → ogrepy-1.3.1}/OGRePy.egg-info/SOURCES.txt +0 -0
- {ogrepy-1.2.0 → ogrepy-1.3.1}/OGRePy.egg-info/dependency_links.txt +0 -0
- {ogrepy-1.2.0 → ogrepy-1.3.1}/OGRePy.egg-info/requires.txt +0 -0
- {ogrepy-1.2.0 → ogrepy-1.3.1}/OGRePy.egg-info/top_level.txt +0 -0
- {ogrepy-1.2.0 → ogrepy-1.3.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
r"""
|
|
2
2
|
# OGRePy: An Object-Oriented General Relativity Package for Python
|
|
3
|
-
v1.
|
|
3
|
+
v1.3.1 (2025-08-03)
|
|
4
4
|
|
|
5
5
|
By **Barak Shoshany**\
|
|
6
6
|
Email: <baraksh@gmail.com>\
|
|
@@ -12,24 +12,29 @@ PyPi project: <https://pypi.org/project/OGRePy/>
|
|
|
12
12
|
|
|
13
13
|
Based on the Mathematica package [OGRe](https://github.com/bshoshany/OGRe) by Barak Shoshany.
|
|
14
14
|
|
|
15
|
-
Copyright (c)
|
|
15
|
+
Copyright (c) 2025 [Barak Shoshany](https://baraksh.com/). Licensed under the [MIT license](https://github.com/bshoshany/OGRePy/blob/master/LICENSE.txt).
|
|
16
16
|
|
|
17
17
|
If you use this package in software of any kind, please provide a link to [the GitHub repository](https://github.com/bshoshany/OGRePy) in the source code and documentation.
|
|
18
18
|
|
|
19
19
|
If you use this package in published research, please cite it as follows:
|
|
20
20
|
|
|
21
|
-
* Barak Shoshany, *"OGRePy: An Object-Oriented General Relativity Package for Python"*, [doi:10.
|
|
21
|
+
* Barak Shoshany, *"OGRePy: An Object-Oriented General Relativity Package for Python"*, [Journal of Open Research Software 13: 9](https://openresearchsoftware.metajnl.com/articles/10.5334/jors.558), [doi:10.5334/jors.558](https://doi.org/10.5334/jors.558), [arXiv:2409.03803](https://arxiv.org/abs/2409.03803) (July 2025)
|
|
22
22
|
|
|
23
23
|
You can use the following BibTeX entry:
|
|
24
24
|
|
|
25
25
|
```bibtex
|
|
26
|
-
@article{
|
|
26
|
+
@article{ShoshanyOGRePy,
|
|
27
27
|
archiveprefix = {arXiv},
|
|
28
28
|
author = {Barak Shoshany},
|
|
29
|
-
doi = {10.
|
|
29
|
+
doi = {10.5334/jors.558},
|
|
30
30
|
eprint = {2409.03803},
|
|
31
|
+
issn = {2049-9647},
|
|
32
|
+
journal = {Journal of Open Research Software},
|
|
33
|
+
pages = {9},
|
|
34
|
+
publisher = {Ubiquity Press, Ltd.},
|
|
31
35
|
title = {{OGRePy: An Object-Oriented General Relativity Package for Python}},
|
|
32
|
-
|
|
36
|
+
volume = {13},
|
|
37
|
+
year = {2025},
|
|
33
38
|
}
|
|
34
39
|
```
|
|
35
40
|
|
|
@@ -43,4 +48,4 @@ import sympy as s
|
|
|
43
48
|
from ._core import Coordinates, CovariantD, Metric, OGRePyError, PartialD, Tensor, __version__, calc, cite, compare, diag, doc, func, info, options, release_date, sym, syms, update_check, welcome
|
|
44
49
|
|
|
45
50
|
# The names that will be exported if using `from OGRePy import *`. Contains exactly all the names imported above.
|
|
46
|
-
__all__: list[str] = ["
|
|
51
|
+
__all__: list[str] = ["Coordinates", "CovariantD", "Metric", "OGRePyError", "PartialD", "Tensor", "__version__", "calc", "cite", "compare", "diag", "doc", "func", "info", "options", "release_date", "s", "sym", "syms", "update_check", "welcome"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
r"""
|
|
2
2
|
# OGRePy: An Object-Oriented General Relativity Package for Python
|
|
3
|
-
v1.
|
|
3
|
+
v1.3.1 (2025-08-03)
|
|
4
4
|
|
|
5
5
|
By **Barak Shoshany**\
|
|
6
6
|
Email: <baraksh@gmail.com>\
|
|
@@ -12,24 +12,29 @@ PyPi project: <https://pypi.org/project/OGRePy/>
|
|
|
12
12
|
|
|
13
13
|
Based on the Mathematica package [OGRe](https://github.com/bshoshany/OGRe) by Barak Shoshany.
|
|
14
14
|
|
|
15
|
-
Copyright (c)
|
|
15
|
+
Copyright (c) 2025 [Barak Shoshany](https://baraksh.com/). Licensed under the [MIT license](https://github.com/bshoshany/OGRePy/blob/master/LICENSE.txt).
|
|
16
16
|
|
|
17
17
|
If you use this package in software of any kind, please provide a link to [the GitHub repository](https://github.com/bshoshany/OGRePy) in the source code and documentation.
|
|
18
18
|
|
|
19
19
|
If you use this package in published research, please cite it as follows:
|
|
20
20
|
|
|
21
|
-
* Barak Shoshany, *"OGRePy: An Object-Oriented General Relativity Package for Python"*, [doi:10.
|
|
21
|
+
* Barak Shoshany, *"OGRePy: An Object-Oriented General Relativity Package for Python"*, [Journal of Open Research Software 13: 9](https://openresearchsoftware.metajnl.com/articles/10.5334/jors.558), [doi:10.5334/jors.558](https://doi.org/10.5334/jors.558), [arXiv:2409.03803](https://arxiv.org/abs/2409.03803) (July 2025)
|
|
22
22
|
|
|
23
23
|
You can use the following BibTeX entry:
|
|
24
24
|
|
|
25
25
|
```bibtex
|
|
26
|
-
@article{
|
|
26
|
+
@article{ShoshanyOGRePy,
|
|
27
27
|
archiveprefix = {arXiv},
|
|
28
28
|
author = {Barak Shoshany},
|
|
29
|
-
doi = {10.
|
|
29
|
+
doi = {10.5334/jors.558},
|
|
30
30
|
eprint = {2409.03803},
|
|
31
|
+
issn = {2049-9647},
|
|
32
|
+
journal = {Journal of Open Research Software},
|
|
33
|
+
pages = {9},
|
|
34
|
+
publisher = {Ubiquity Press, Ltd.},
|
|
31
35
|
title = {{OGRePy: An Object-Oriented General Relativity Package for Python}},
|
|
32
|
-
|
|
36
|
+
volume = {13},
|
|
37
|
+
year = {2025},
|
|
33
38
|
}
|
|
34
39
|
```
|
|
35
40
|
|
|
@@ -56,6 +61,7 @@ import inspect
|
|
|
56
61
|
import itertools
|
|
57
62
|
import json
|
|
58
63
|
import os
|
|
64
|
+
import pathlib
|
|
59
65
|
import re
|
|
60
66
|
import sys
|
|
61
67
|
import urllib.request
|
|
@@ -106,8 +112,8 @@ class OGRePyError(Exception):
|
|
|
106
112
|
####################
|
|
107
113
|
|
|
108
114
|
|
|
109
|
-
__version__: str = "1.
|
|
110
|
-
release_date: str = "
|
|
115
|
+
__version__: str = "1.3.1"
|
|
116
|
+
release_date: str = "2025-08-03"
|
|
111
117
|
|
|
112
118
|
|
|
113
119
|
####################
|
|
@@ -148,18 +154,21 @@ def cite() -> None:
|
|
|
148
154
|
inspect.cleandoc("""
|
|
149
155
|
If you use this package in published research, please cite it as follows:
|
|
150
156
|
|
|
151
|
-
* Barak Shoshany, *"OGRePy: An Object-Oriented General Relativity Package for Python"*, [doi:10.
|
|
157
|
+
* Barak Shoshany, *"OGRePy: An Object-Oriented General Relativity Package for Python"*, [Journal of Open Research Software 13: 9](https://openresearchsoftware.metajnl.com/articles/10.5334/jors.558), [doi:10.5334/jors.558](https://doi.org/10.5334/jors.558), [arXiv:2409.03803](https://arxiv.org/abs/2409.03803) (July 2025)
|
|
152
158
|
|
|
153
159
|
You can use the following BibTeX entry:
|
|
154
160
|
|
|
155
161
|
```bibtex
|
|
156
|
-
@article{
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
162
|
+
@article{Shoshany2025_OGRePy,
|
|
163
|
+
author = {Barak Shoshany},
|
|
164
|
+
doi = {10.5334/jors.558},
|
|
165
|
+
issn = {2049-9647},
|
|
166
|
+
journal = {Journal of Open Research Software},
|
|
167
|
+
publisher = {Ubiquity Press, Ltd.},
|
|
168
|
+
title = {{OGRePy: An Object-Oriented General Relativity Package for Python}},
|
|
169
|
+
url = {http://dx.doi.org/10.5334/jors.558},
|
|
170
|
+
volume = {13},
|
|
171
|
+
year = {2025},
|
|
163
172
|
}
|
|
164
173
|
```
|
|
165
174
|
|
|
@@ -250,11 +259,11 @@ def doc(
|
|
|
250
259
|
try:
|
|
251
260
|
signature: str = str(inspect.signature(obj)).replace("'", "")
|
|
252
261
|
except (ValueError, TypeError):
|
|
253
|
-
_handle_error(f"Could not find call signature for `{name if name !=
|
|
262
|
+
_handle_error(f"Could not find call signature for `{name if name != '' else 'this object'}`.")
|
|
254
263
|
# Print the object's name, then its signature, then the docstring itself. Types in the signature will be surrounded by single quotes, so we eliminate those before printing.
|
|
255
264
|
_display_markdown('<div style="border: 1px solid; margin: auto; padding: 1em; width: 90%">\n\n`' + name + signature + "`\n\n" + docs + "\n\n</div>")
|
|
256
265
|
else:
|
|
257
|
-
_handle_error(f"Could not find documentation for `{name if name !=
|
|
266
|
+
_handle_error(f"Could not find documentation for `{name if name != '' else 'this object'}`.")
|
|
258
267
|
|
|
259
268
|
|
|
260
269
|
def func(
|
|
@@ -377,24 +386,50 @@ def welcome() -> None:
|
|
|
377
386
|
"""
|
|
378
387
|
Print the welcome message.
|
|
379
388
|
"""
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
389
|
+
ipynb_filename: str = ""
|
|
390
|
+
pdf_filename: str = ""
|
|
391
|
+
html_filename: str = ""
|
|
392
|
+
if "pyodide" not in sys.modules:
|
|
393
|
+
# The documentation files are bundled with the package so that they can be viewed offline. We make sure they exist and get their paths.
|
|
394
|
+
with importlib.resources.as_file(importlib.resources.files().joinpath("docs/OGRePy_Documentation")) as file:
|
|
395
|
+
ipynb_file: pathlib.Path = file.with_suffix(".ipynb")
|
|
396
|
+
if ipynb_file.exists():
|
|
397
|
+
ipynb_filename = ipynb_file.as_posix()
|
|
398
|
+
pdf_file: pathlib.Path = file.with_suffix(".pdf")
|
|
399
|
+
if pdf_file.exists():
|
|
400
|
+
pdf_filename = pdf_file.as_posix()
|
|
401
|
+
html_file: pathlib.Path = file.with_suffix(".html")
|
|
402
|
+
if html_file.exists():
|
|
403
|
+
html_filename = html_file.as_uri()
|
|
404
|
+
else:
|
|
405
|
+
# If we're running in a JupyterLite notebook, we will not be able to access the documentation files directly. However, if using OGRePyLive, the files should be available in the same folder as the notebook, so we check for that.
|
|
406
|
+
file = pathlib.Path("./OGRePy_Documentation")
|
|
407
|
+
ipynb_file: pathlib.Path = file.with_suffix(".ipynb")
|
|
408
|
+
if ipynb_file.exists():
|
|
409
|
+
ipynb_filename = ipynb_file.as_posix()
|
|
410
|
+
pdf_file: pathlib.Path = file.with_suffix(".pdf")
|
|
411
|
+
if pdf_file.exists():
|
|
412
|
+
pdf_filename = pdf_file.as_posix()
|
|
413
|
+
html_file: pathlib.Path = file.with_suffix(".html")
|
|
414
|
+
if html_file.exists():
|
|
415
|
+
html_filename = html_file.as_posix()
|
|
416
|
+
ipynb_link: str = f"""<a href="{ipynb_filename if ipynb_filename != "" else "https://github.com/bshoshany/OGRePy/blob/master/OGRePy/docs/OGRePy_Documentation.ipynb"}">.ipynb</a>"""
|
|
417
|
+
pdf_link: str = f"""<a href="{pdf_filename if pdf_filename != "" else "https://github.com/bshoshany/OGRePy/blob/master/OGRePy/docs/OGRePy_Documentation.pdf"}">.pdf</a>"""
|
|
418
|
+
html_url: str = html_filename if html_filename != "" else "https://raw.githack.com/bshoshany/OGRePy/master/OGRePy/docs/OGRePy_Documentation.html"
|
|
419
|
+
html_link: str = f"""<a href="#" onclick="window.open('{html_url}', '_blank')">.html</a>""" if "pyodide" not in sys.modules else f"""<a href="{html_url}">.html</a>"""
|
|
420
|
+
# Display the welcome message.
|
|
421
|
+
_display_markdown(
|
|
422
|
+
inspect.cleandoc(rf"""
|
|
423
|
+
**OGRePy: An <u>O</u>bject-Oriented <u>G</u>eneral <u>Re</u>lativity Package for <u>Py</u>thon\
|
|
424
|
+
By [Barak Shoshany](https://github.com/bshoshany) ([baraksh@gmail.com](mailto:baraksh@gmail.com)) ([baraksh.com](https://baraksh.com/))\
|
|
425
|
+
v{__version__} ({release_date})\
|
|
426
|
+
GitHub repository: <https://github.com/bshoshany/OGRePy>\
|
|
427
|
+
Documentation: {ipynb_link}, {pdf_link}, {html_link}**
|
|
428
|
+
"""),
|
|
429
|
+
)
|
|
430
|
+
# If we're not in a notebook, warn that the package should be used inside a notebook.
|
|
431
|
+
if not _in_notebook:
|
|
432
|
+
_display_markdown("WARNING: No notebook interface detected! This package was designed to be used inside a Jupyter notebook in Visual Studio Code or JupyterLab.")
|
|
398
433
|
|
|
399
434
|
|
|
400
435
|
##################
|
|
@@ -537,7 +572,7 @@ class Coordinates:
|
|
|
537
572
|
text: str = f"* **Name**: {_lookup_names_string(self)}\n"
|
|
538
573
|
text += f"* **Class**: {type(self).__name__}\n"
|
|
539
574
|
text += f"* **Dimensions**: {self.dim()}\n"
|
|
540
|
-
text += f"* **Default Coordinates For**: {
|
|
575
|
+
text += f"* **Default Coordinates For**: {', '.join(_using_coords(self))}\n"
|
|
541
576
|
_display_markdown(text)
|
|
542
577
|
|
|
543
578
|
def inverse_jacobian(
|
|
@@ -837,7 +872,7 @@ class CovariantD:
|
|
|
837
872
|
dummy_christoffel: str = _to_tex(s.Dummy())
|
|
838
873
|
for pos, (index_letter, index_type) in enumerate(zip(tensor_letters, use_indices, strict=True)):
|
|
839
874
|
# Replace the current index with the dummy index in the tensor's index specification.
|
|
840
|
-
index_replaced: IndexSpecification = tensor_letters[0:pos]
|
|
875
|
+
index_replaced: IndexSpecification = [*tensor_letters[0:pos], dummy_christoffel, *tensor_letters[pos + 1 : rank]]
|
|
841
876
|
if index_type == 1:
|
|
842
877
|
# For an upper index, add the Christoffel symbols with their third index contracted with the tensor.
|
|
843
878
|
out_tensor += other(*index_replaced) @ use_christoffel(index_letter, dummy_covariant, dummy_christoffel)
|
|
@@ -1038,7 +1073,7 @@ class Tensor:
|
|
|
1038
1073
|
output_tensor._symbol = self._symbol + sign + other_symbol
|
|
1039
1074
|
return output_tensor
|
|
1040
1075
|
|
|
1041
|
-
def __call__(
|
|
1076
|
+
def __call__( # noqa: RET503
|
|
1042
1077
|
self: Self,
|
|
1043
1078
|
*letters: str | s.Symbol,
|
|
1044
1079
|
) -> Self | Tensor:
|
|
@@ -1053,14 +1088,14 @@ class Tensor:
|
|
|
1053
1088
|
calc_letters: IndexSpecification = _collect_tex_symbols(*letters)
|
|
1054
1089
|
# Check that the index specification matches the rank of the tensor.
|
|
1055
1090
|
if len(calc_letters) != self.rank():
|
|
1056
|
-
_handle_error(f"{f
|
|
1091
|
+
_handle_error(f"{f'The index specification\n${"".join(calc_letters)}$\n' if len(calc_letters) > 0 else 'The empty index specification '}does not match the rank of the tensor. The number of indices should be {self.rank()}.")
|
|
1057
1092
|
# Check for duplicate index letters.
|
|
1058
1093
|
tally: dict[str, int] = {letter: calc_letters.count(letter) for letter in set(calc_letters)}
|
|
1059
1094
|
max_duplicates: int = max(tally.values()) if len(tally) > 0 else 0
|
|
1060
1095
|
if max_duplicates > 2:
|
|
1061
1096
|
# We can't have more than 2 instances of the same index.
|
|
1062
1097
|
invalid_indices: IndexSpecification = [letter for letter, count in tally.items() if count > 2]
|
|
1063
|
-
_handle_error(f"The index specification\n${
|
|
1098
|
+
_handle_error(f"The index specification\n${''.join(calc_letters)}$\nis invalid, as it contains more than two instances of the {'index' if len(invalid_indices) == 1 else 'indices'} \n${', '.join(invalid_indices)}$\n.")
|
|
1064
1099
|
elif max_duplicates == 2:
|
|
1065
1100
|
# If any indices appear exactly twice, we need to trace them.
|
|
1066
1101
|
trace_letters: IndexSpecification = [letter for letter, count in tally.items() if count == 2]
|
|
@@ -1161,7 +1196,7 @@ class Tensor:
|
|
|
1161
1196
|
# Count how many summation indices there already are in the first tensor's symbol (from previous contractions).
|
|
1162
1197
|
contract_count: int = len(_unique_summation_placeholders(self._symbol))
|
|
1163
1198
|
# Increase the numbering of the summation indices as well.
|
|
1164
|
-
second_symbol = _summation_pattern.sub(lambda match: f"[{match[1] if match[1] else
|
|
1199
|
+
second_symbol = _summation_pattern.sub(lambda match: f"[{match[1] if match[1] else ''}{int(match[2]) + contract_count}{match[3] if match[3] else ''}]", second_symbol)
|
|
1165
1200
|
# Add the number of contractions in the second symbol to the total count.
|
|
1166
1201
|
contract_count += len(_unique_summation_placeholders(second_symbol))
|
|
1167
1202
|
# Join the symbols of the two tensors to create a new symbol for the output tensor, with consecutive summation index placeholders.
|
|
@@ -1420,7 +1455,7 @@ class Tensor:
|
|
|
1420
1455
|
if coords is None:
|
|
1421
1456
|
warning = f"Using default coordinate system {_lookup_names_string(use_coords)}"
|
|
1422
1457
|
if indices is None:
|
|
1423
|
-
warning += f"{
|
|
1458
|
+
warning += f"{'Using' if warning == '' else ' and'} default index configuration {use_indices}"
|
|
1424
1459
|
if warning != "":
|
|
1425
1460
|
_display_markdown(f"**OGRePy**: {warning}.")
|
|
1426
1461
|
# Retrieve the components, calculating them if they have not already been calculated.
|
|
@@ -1469,7 +1504,7 @@ class Tensor:
|
|
|
1469
1504
|
text += f"* **Metric**: {_lookup_names_string(self._metric)}\n"
|
|
1470
1505
|
else:
|
|
1471
1506
|
used_by: list[str] = _using_metric(self)
|
|
1472
|
-
text += f"* **Associated Metric For**: {
|
|
1507
|
+
text += f"* **Associated Metric For**: {', '.join(used_by) if len(used_by) > 0 else 'None'}\n"
|
|
1473
1508
|
_display_markdown(text)
|
|
1474
1509
|
|
|
1475
1510
|
def list(
|
|
@@ -1715,7 +1750,7 @@ class Tensor:
|
|
|
1715
1750
|
out: str = r"\begin{align*}" + "\n"
|
|
1716
1751
|
# Create an equation for each unique element. Note that in an {align*} environment, \\ indicates the end of a line and & indicates the position where the equations will be vertically aligned.
|
|
1717
1752
|
for key, value in non_zero.items():
|
|
1718
|
-
out += rf" {
|
|
1753
|
+
out += rf" {' = '.join(value)} &= {_to_tex(key)} \\" + "\n"
|
|
1719
1754
|
# Remove the last end of line indicator (otherwise there will be an extra empty line) and end the {align*} environment, then return the generated TeX code.
|
|
1720
1755
|
return out[:-3] + "\n" + r"\end{align*}"
|
|
1721
1756
|
|
|
@@ -1831,9 +1866,10 @@ class Tensor:
|
|
|
1831
1866
|
# Replace any derivative with respect to the curve parameter with a more compact partial derivative symbol, and enclose the argument in parentheses (which SymPy doesn't do for some reason).
|
|
1832
1867
|
components = _array_subs(components, {f: sym(r"\partial_{" + _to_tex(param) + r"} \left(" + _to_tex(f.args[0]) + r"\right)") for f in components.atoms(s.Derivative) if f.args[1] == (param, 1)})
|
|
1833
1868
|
# For all tensors, replace any derivative with respect to the coordinate symbols with a more compact partial derivative symbol.
|
|
1834
|
-
|
|
1835
|
-
components
|
|
1836
|
-
|
|
1869
|
+
return _array_subs(
|
|
1870
|
+
components,
|
|
1871
|
+
{f: sym(r"\partial_{" + _to_tex(x) + ((r"^{" + str(f.args[1][1]) + r"}") if f.args[1][1] > 1 else "") + r"} " + _to_tex(f.args[0])) for x in coord_symbols for f in components.atoms(s.Derivative) if f.args[1][0] == x and f.args[1][1] > 0},
|
|
1872
|
+
)
|
|
1837
1873
|
|
|
1838
1874
|
def _get_components(
|
|
1839
1875
|
self: Self,
|
|
@@ -2261,7 +2297,7 @@ class Einstein(Tensor, FixedDefaultIndices):
|
|
|
2261
2297
|
#### Returns:
|
|
2262
2298
|
The calculated components.
|
|
2263
2299
|
"""
|
|
2264
|
-
return (metric.ricci_tensor("mu nu") - s.Rational(1, 2) * metric.ricci_scalar() @ metric("mu nu"))._calc_representation(indices=(-1, -1), coords=coords)
|
|
2300
|
+
return cast(s.Array, (metric.ricci_tensor("mu nu") - s.Rational(1, 2) * metric.ricci_scalar() @ metric("mu nu"))._calc_representation(indices=(-1, -1), coords=coords))
|
|
2265
2301
|
|
|
2266
2302
|
|
|
2267
2303
|
class GeodesicFromChristoffel(Tensor, CleanupCurveParameter, FixedDefaultIndices):
|
|
@@ -2476,9 +2512,9 @@ class GeodesicTimeParam(Tensor, CleanupTimeParameter, FixedDefaultIndices):
|
|
|
2476
2512
|
# Use the first coordinate as the curve parameter.
|
|
2477
2513
|
time: s.Symbol = cast(s.Symbol, coords.components()[0])
|
|
2478
2514
|
# Create a tangent vector whose components are the first derivatives of the coordinates with respect to t (thus the first component will be equal to 1).
|
|
2479
|
-
tangent: s.Array = s.Array([1
|
|
2515
|
+
tangent: s.Array = s.Array([1, *coords.of_param_dot(time)[1:]])
|
|
2480
2516
|
# Create an acceleration vector whose components are the second derivatives of the coordinates with respect to t (thus the first component will be equal to 0).
|
|
2481
|
-
accel: s.Array = s.Array([0
|
|
2517
|
+
accel: s.Array = s.Array([0, *coords.of_param_ddot(time)[1:]])
|
|
2482
2518
|
# Obtain the Christoffel symbols, and replace any instance of the spatial coordinate symbols with coordinate functions of time.
|
|
2483
2519
|
param_dict: dict[s.Symbol, AppliedUndef] = coords.of_param_dict(time)
|
|
2484
2520
|
del param_dict[time]
|
|
@@ -3389,7 +3425,7 @@ def _list_aliases(
|
|
|
3389
3425
|
return f"`{names[0]}`"
|
|
3390
3426
|
if len(names) == 2:
|
|
3391
3427
|
return f"`{names[0]}` (alias: `{names[1]}`)"
|
|
3392
|
-
return f"`{names[0]}` (aliases: `{
|
|
3428
|
+
return f"`{names[0]}` (aliases: `{'`, `'.join(names[1:])}`)"
|
|
3393
3429
|
|
|
3394
3430
|
|
|
3395
3431
|
def _list_references(
|
|
@@ -3412,11 +3448,11 @@ def _list_references(
|
|
|
3412
3448
|
if isinstance(ref, Coordinates):
|
|
3413
3449
|
using: list[str] = _using_coords(ref)
|
|
3414
3450
|
if len(using) > 0:
|
|
3415
|
-
text += f", default for: `{
|
|
3451
|
+
text += f", default for: `{'`, `'.join(using)}`"
|
|
3416
3452
|
elif isinstance(ref, Metric):
|
|
3417
3453
|
using: list[str] = _using_metric(ref)
|
|
3418
3454
|
if len(using) > 0:
|
|
3419
|
-
text += f", used by: `{
|
|
3455
|
+
text += f", used by: `{'`, `'.join(using)}`"
|
|
3420
3456
|
text += "\n"
|
|
3421
3457
|
return text
|
|
3422
3458
|
|
|
@@ -3624,7 +3660,7 @@ def _using_object(
|
|
|
3624
3660
|
# Create a list of relevant tensors, including aliases if any.
|
|
3625
3661
|
using: list[str] = []
|
|
3626
3662
|
for tensor, names in tensor_reverse.items():
|
|
3627
|
-
if isinstance(obj, Coordinates) and obj is tensor.default_coords or isinstance(obj, Metric) and obj is tensor.metric():
|
|
3663
|
+
if (isinstance(obj, Coordinates) and obj is tensor.default_coords) or (isinstance(obj, Metric) and obj is tensor.metric()):
|
|
3628
3664
|
using.append(_list_aliases(names))
|
|
3629
3665
|
return using
|
|
3630
3666
|
|
|
@@ -3734,13 +3770,13 @@ def _validate_permutation(
|
|
|
3734
3770
|
letters: IndexSpecification = _collect_tex_symbols(*index_spec)
|
|
3735
3771
|
# Check that the index specifications matches the rank of the tensor.
|
|
3736
3772
|
if len(letters) != rank:
|
|
3737
|
-
_handle_error(f"The index specification\n${
|
|
3773
|
+
_handle_error(f"The index specification\n${''.join(letters)}$\ndoes not match the rank of the tensor. The number of indices should be {rank}.")
|
|
3738
3774
|
# Check for duplicate index letters.
|
|
3739
3775
|
tally: dict[str, int] = {letter: letters.count(letter) for letter in set(letters)}
|
|
3740
3776
|
max_duplicates: int = max(tally.values()) if len(tally) > 0 else 0
|
|
3741
3777
|
if max_duplicates > 1:
|
|
3742
3778
|
invalid_indices: IndexSpecification = [letter for letter, count in tally.items() if count > 1]
|
|
3743
|
-
_handle_error(f"The index specification\n${
|
|
3779
|
+
_handle_error(f"The index specification\n${''.join(letters)}$\nis invalid, as it contains more than one instance of the {'index' if len(invalid_indices) == 1 else 'indices'} \n${', '.join(invalid_indices)}$\n.")
|
|
3744
3780
|
# Return the letters as a list of TeX strings.
|
|
3745
3781
|
return letters
|
|
3746
3782
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
r"""
|
|
2
2
|
# OGRePy: An Object-Oriented General Relativity Package for Python
|
|
3
|
-
v1.
|
|
3
|
+
v1.3.1 (2025-08-03)
|
|
4
4
|
|
|
5
5
|
By **Barak Shoshany**\
|
|
6
6
|
Email: <baraksh@gmail.com>\
|
|
@@ -12,24 +12,29 @@ PyPi project: <https://pypi.org/project/OGRePy/>
|
|
|
12
12
|
|
|
13
13
|
Based on the Mathematica package [OGRe](https://github.com/bshoshany/OGRe) by Barak Shoshany.
|
|
14
14
|
|
|
15
|
-
Copyright (c)
|
|
15
|
+
Copyright (c) 2025 [Barak Shoshany](https://baraksh.com/). Licensed under the [MIT license](https://github.com/bshoshany/OGRePy/blob/master/LICENSE.txt).
|
|
16
16
|
|
|
17
17
|
If you use this package in software of any kind, please provide a link to [the GitHub repository](https://github.com/bshoshany/OGRePy) in the source code and documentation.
|
|
18
18
|
|
|
19
19
|
If you use this package in published research, please cite it as follows:
|
|
20
20
|
|
|
21
|
-
* Barak Shoshany, *"OGRePy: An Object-Oriented General Relativity Package for Python"*, [doi:10.
|
|
21
|
+
* Barak Shoshany, *"OGRePy: An Object-Oriented General Relativity Package for Python"*, [Journal of Open Research Software 13: 9](https://openresearchsoftware.metajnl.com/articles/10.5334/jors.558), [doi:10.5334/jors.558](https://doi.org/10.5334/jors.558), [arXiv:2409.03803](https://arxiv.org/abs/2409.03803) (July 2025)
|
|
22
22
|
|
|
23
23
|
You can use the following BibTeX entry:
|
|
24
24
|
|
|
25
25
|
```bibtex
|
|
26
|
-
@article{
|
|
26
|
+
@article{ShoshanyOGRePy,
|
|
27
27
|
archiveprefix = {arXiv},
|
|
28
28
|
author = {Barak Shoshany},
|
|
29
|
-
doi = {10.
|
|
29
|
+
doi = {10.5334/jors.558},
|
|
30
30
|
eprint = {2409.03803},
|
|
31
|
+
issn = {2049-9647},
|
|
32
|
+
journal = {Journal of Open Research Software},
|
|
33
|
+
pages = {9},
|
|
34
|
+
publisher = {Ubiquity Press, Ltd.},
|
|
31
35
|
title = {{OGRePy: An Object-Oriented General Relativity Package for Python}},
|
|
32
|
-
|
|
36
|
+
volume = {13},
|
|
37
|
+
year = {2025},
|
|
33
38
|
}
|
|
34
39
|
```
|
|
35
40
|
|