Compare commits

..

8 Commits

Author SHA1 Message Date
Nicolas cc1057a99b CD for pypi: environment added 2025-07-28 17:11:23 +02:00
Nicolas e2a6541119 Docs: Link to repo added 2025-07-28 17:11:23 +02:00
Nicolas dd86a1a83a doc path renamed to "api" 2025-07-28 17:11:23 +02:00
Nicolas Kruse 38709c03ac md_to_code.py updated to handle print functions in the last line 2025-07-28 15:06:32 +02:00
Nicolas Kruse 77b80a31fc sofc example fixes of the extension 2025-07-28 15:06:32 +02:00
Nicolas 138e33e5b6 sofc example extended 2025-07-28 15:06:32 +02:00
Nicolas 14bd6605bc switched docs deployment method 2025-07-28 15:06:32 +02:00
Nicolas eb171fd424 fix in SOEC example 2025-07-28 15:06:32 +02:00
12 changed files with 70 additions and 23 deletions

View File

@ -14,7 +14,7 @@ exclude =
dist, dist,
.conda, .conda,
tests/autogenerated_*, tests/autogenerated_*,
docs/source/_autogenerated docs/source/api
.venv, .venv,
venv venv

View File

@ -9,7 +9,7 @@ permissions:
contents: write contents: write
jobs: jobs:
build-and-deploy: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -41,8 +41,24 @@ jobs:
rm ./source/*.rst rm ./source/*.rst
make html make html
touch ./build/html/.nojekyll touch ./build/html/.nojekyll
- name: Deploy to GitHub Pages mkdir -p ./build/html/_autogenerated
uses: JamesIves/github-pages-deploy-action@v4 cp ./build/html/api/* ./build/html/_autogenerated/
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with: with:
branch: gh-pages path: docs/build/html
folder: docs/build/html
deploy:
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

View File

@ -10,6 +10,10 @@ jobs:
name: Build and publish name: Build and publish
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/project/${{ github.event.repository.name }}/
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

2
.gitignore vendored
View File

@ -9,7 +9,7 @@ __pycache__
.pytest_cache .pytest_cache
tests/autogenerated_*.py tests/autogenerated_*.py
docs/build/ docs/build/
docs/source/_autogenerated/ docs/source/api/
venv/ venv/
.venv/ .venv/
thermo_data/combined_data.yaml thermo_data/combined_data.yaml

View File

@ -27,7 +27,7 @@ def write_classes(f: TextIOWrapper, patterns: list[str], module_name: str, title
write_dochtree(f, title, classes) write_dochtree(f, title, classes)
for cls in classes: for cls in classes:
with open(f'docs/source/_autogenerated/{cls}.md', 'w') as f2: with open(f'docs/source/api/{cls}.md', 'w') as f2:
f2.write(f'# {module_name}.{cls}\n') f2.write(f'# {module_name}.{cls}\n')
f2.write('```{eval-rst}\n') f2.write('```{eval-rst}\n')
f2.write(f'.. autoclass:: {module_name}.{cls}\n') f2.write(f'.. autoclass:: {module_name}.{cls}\n')
@ -43,7 +43,7 @@ def write_functions(f: TextIOWrapper, patterns: list[str], module_name: str, tit
module = importlib.import_module(module_name) module = importlib.import_module(module_name)
functions = [ functions = [
name for name, obj in inspect.getmembers(module, inspect.isfunction) name for name, _ in inspect.getmembers(module, inspect.isfunction)
if (any(fnmatch.fnmatch(name, pat) for pat in patterns if pat not in exclude)) if (any(fnmatch.fnmatch(name, pat) for pat in patterns if pat not in exclude))
] ]
@ -54,7 +54,7 @@ def write_functions(f: TextIOWrapper, patterns: list[str], module_name: str, tit
for func in functions: for func in functions:
if not func.startswith('_'): if not func.startswith('_'):
with open(f'docs/source/_autogenerated/{func}.md', 'w') as f2: with open(f'docs/source/api/{func}.md', 'w') as f2:
f2.write(f'# {module_name}.{func}\n') f2.write(f'# {module_name}.{func}\n')
f2.write('```{eval-rst}\n') f2.write('```{eval-rst}\n')
f2.write(f'.. autofunction:: {module_name}.{func}\n') f2.write(f'.. autofunction:: {module_name}.{func}\n')
@ -74,9 +74,9 @@ def write_dochtree(f: TextIOWrapper, title: str, items: list[str]):
if __name__ == "__main__": if __name__ == "__main__":
# Ensure the output directory exists # Ensure the output directory exists
os.makedirs('docs/source/_autogenerated', exist_ok=True) os.makedirs('docs/source/api', exist_ok=True)
with open('docs/source/_autogenerated/index.md', 'w') as f: with open('docs/source/api/index.md', 'w') as f:
f.write('# Classes and functions\n\n') f.write('# Classes and functions\n\n')
write_classes(f, ['*'], 'gaspype', title='Classes') write_classes(f, ['*'], 'gaspype', title='Classes')

View File

@ -1,8 +1,9 @@
```{toctree} ```{toctree}
:maxdepth: 1 :maxdepth: 1
:hidden: :hidden:
_autogenerated/index api/index
_autogenerated/examples api/examples
repo
``` ```
```{include} ../../README.md ```{include} ../../README.md

View File

@ -51,7 +51,7 @@ def render_examples(filter: str, example_file: str):
f.write('## Download Jupyter Notebooks\n\n') f.write('## Download Jupyter Notebooks\n\n')
for path, name in zip(files, names): for path, name in zip(files, names):
if name.lower() != 'readme': if name.lower() != 'readme':
run_rendering(path, 'docs/source/_autogenerated') run_rendering(path, 'docs/source/api')
notebook = name + '.ipynb' notebook = name + '.ipynb'
f.write(f'- [{notebook}]({notebook})\n\n') f.write(f'- [{notebook}]({notebook})\n\n')
@ -63,4 +63,4 @@ def render_examples(filter: str, example_file: str):
if __name__ == "__main__": if __name__ == "__main__":
render_examples('examples/*.md', 'docs/source/_autogenerated/examples.md') render_examples('examples/*.md', 'docs/source/api/examples.md')

3
docs/source/repo.md Normal file
View File

@ -0,0 +1,3 @@
# Code repository
Code repository is on GitHub: [github.com/DLR-Institute-of-Future-Fuels/gaspype](https://github.com/DLR-Institute-of-Future-Fuels/gaspype).

View File

@ -11,11 +11,11 @@ The conversion is done like the following automated by the
[docs/source/render_examples.py](../docs/source/render_examples.py) script: [docs/source/render_examples.py](../docs/source/render_examples.py) script:
``` bash ``` bash
# Converting markdown with code sections to Jupyter Notebook and run it: # Converting markdown with code sections to Jupyter Notebook and run it:
notedown examples/soec_methane.md --to notebook --output docs/source/_autogenerated/soec_methane.ipynb --run notedown examples/soec_methane.md --to notebook --output docs/source/api/soec_methane.ipynb --run
# Converting the Jupyter Notebook to Markdown and a folder with image # Converting the Jupyter Notebook to Markdown and a folder with image
# files placed in docs/source/_autogenerated/: # files placed in docs/source/api/:
jupyter nbconvert --to markdown docs/source/_autogenerated/soec_methane.ipynb --output soec_methane.md jupyter nbconvert --to markdown docs/source/api/soec_methane.ipynb --output soec_methane.md
``` ```
A new example Markdown file can be created from a Jupyter Notebook running A new example Markdown file can be created from a Jupyter Notebook running

View File

@ -1,7 +1,7 @@
# SOEC Co-Electrolysis # SOEC Co-Electrolysis
This example shows a 1D isothermal SOEC (Solid oxide electrolyzer cell) model. This example shows a 1D isothermal SOEC (Solid oxide electrolyzer cell) model for
Converting CO2 and H2 into syngas. converting carbon dioxide and steam into syngas.
The operating parameters chosen here are not necessarily realistic. For example, The operating parameters chosen here are not necessarily realistic. For example,
a utilization of 0.95 causes issues with the formation of solid carbon. a utilization of 0.95 causes issues with the formation of solid carbon.
@ -66,13 +66,12 @@ ax.plot(conversion, np.stack([o2_fuel_side, o2_air_side], axis=1), '-')
ax.legend(['o2_fuel_side', 'o2_air_side']) ax.legend(['o2_fuel_side', 'o2_air_side'])
``` ```
The high oxygen partial pressure at the inlet is in reality lower. The high oxygen partial pressure at the inlet is in reality lower.
The assumption that gas inter-diffusion in the flow direction is slower The assumption that gas inter-diffusion in the flow direction is slower
than the gas velocity does not hold at this very high gradient. However than the gas velocity does not hold at this very high gradient. However
often the oxygen partial pressure is still to high to prevent oxidation of the often the oxygen partial pressure is still to high to prevent oxidation of the
cell/electrode. This can be effectively prevented by recycling small amounts of cell/electrode. This can be effectively prevented by recycling small amounts of
the output gas. the hydrogen riche output gas.
Calculation of the local nernst potential between fuel and air side: Calculation of the local nernst potential between fuel and air side:
```python ```python

View File

@ -116,3 +116,26 @@ ax.set_ylabel("Current density / A/cm²")
ax.plot(z_position, node_current, '-') ax.plot(z_position, node_current, '-')
``` ```
Based on the cell current and voltage the energy balance can be calculated.
In the following the electric cell output power (often referred to as "DC power")
and lower heating value (LHV) are calculated. The numbers here are per cell area for
being cell and stack size independent. The quotient of both is often referred to as
LHV based DC efficiency.
```python
dc_power = cell_voltage * terminal_current # W/cm²
print(f"DC power: {dc_power:.2f} W/cm²")
lhv = gp.fluid({'CH4': 1, 'H2O': -2, 'CO2': -1}).get_H(25 + 273.15) # J/mol (LHV of methane)
# LHV based chemical input power:
lhv_power = lhv * terminal_current / (2 * z_O2 * F) # W/cm² (two O2 per CH4 for full oxidation)
efficiency = dc_power / lhv_power
print(f"LHV based DC efficiency: {efficiency*100:.1f} %")
# Or by shortening the therms:
lhv_voltage = lhv / (2 * z_O2 * F) # V
print(f"LHV voltage: {lhv_voltage:.2f} V")
efficiency = cell_voltage / lhv_voltage # LHV based DC efficiency
print(f"LHV based DC efficiency: {efficiency*100:.1f} %")
```

View File

@ -96,6 +96,7 @@ def segments_to_test(segments: Iterable[markdown_segment], script_language: str
ret_block_flag = lines[-1] if (not re.match(r'^[^(]*=', lines[-1]) and ret_block_flag = lines[-1] if (not re.match(r'^[^(]*=', lines[-1]) and
not lines[-1].startswith('import ') and not lines[-1].startswith('import ') and
not lines[-1].startswith('from ') and not lines[-1].startswith('from ') and
not lines[-1].startswith('print(') and
not lines[-1].startswith(' ')) else None not lines[-1].startswith(' ')) else None
# print('Last line: ', ret_block_flag, '-----------', lines[-1]) # print('Last line: ', ret_block_flag, '-----------', lines[-1])