Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,32 @@ PyRPOD utilizies scientific libraries such as NumPy, SciPy, Matplotlib, and SymP
python -m pytest -m verification # verification tests only
python -m pytest -m rpod # a specific subsystem (mdao, mission, plume, rpod)
python -m pytest tests/rpod # or just point pytest at a directory/file directly
```
```

## Logging

PyRPOD ships a formal, opt-in operational logging system built entirely on the
Python standard library. **Importing PyRPOD has no logging side effects** — no
files, directories, handlers, or root-logger changes. Applications turn logging
on explicitly:

```python
from pyrpod.logging_utils import configure_logging

session = configure_logging(case_dir)
try:
# ... run the PyRPOD workflow, e.g. study.jfh_plume_strikes() ...
session.finalize("successful")
finally:
session.close()
```

Runtime logs are written to `<case_dir>/results/logs/<case-name>_<timestamp>.log`
(a new file per run). Behavior is configured per case via an optional
`<case_dir>/logging.ini`, with environment variables (`PYRPOD_LOG_LEVEL`,
`PYRPOD_LOG_FORMAT`) and explicit API arguments taking precedence.

See [docs/logging.md](docs/logging.md) for the full architecture, the
`logging.ini` schema ([example](docs/logging.ini.example)), configuration
precedence, log-level guidance, input checksums, configuration snapshots,
performance/memory caveats, and serial-fallback behavior.
18 changes: 18 additions & 0 deletions case/rpod/1d_approach/logging.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# PyRPOD logging configuration for the 1d_approach RPOD plume-strike case.
#
# Read by pyrpod.logging_utils.configure_logging(case_dir). See docs/logging.md
# and docs/logging.ini.example for the full schema.
#
# Demonstrates a coarser progress cadence: an INFO progress record is emitted
# every 2 firings (and always for the final firing).

[logging]
enabled = true
console = true
file = true
level = INFO
progress_every_n_firings = 2
log_array_stats = true
log_performance = true
snapshot_config = true
checksum_inputs = true
66 changes: 66 additions & 0 deletions case/rpod/1d_approach/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Demonstration driver for the 1d_approach RPOD plume-strike case.

Mirrors tests/rpod/rpod_integration_test_02.py (minus the assertions) and wraps
the workflow in PyRPOD's operational logging. This case models a notional VV
firing its adverse thrusters to slow down along a 1D approach. Run it with:

python case/rpod/1d_approach/run.py

A runtime log is written to case/rpod/1d_approach/results/logs/. Logging
behavior (here, a progress record every 2 firings) is set by logging.ini.
"""
import os
import sys

_HERE = os.path.dirname(os.path.abspath(__file__))
_REPO_ROOT = os.path.abspath(os.path.join(_HERE, "..", "..", ".."))
if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT)

from pyrpod.logging_utils import configure_logging
from pyrpod.rpod import JetFiringHistory, PlumeStrikeEstimationStudy
from pyrpod.vehicle import LogisticsModule, TargetVehicle
from pyrpod.mission import MissionEnvironment

CASE_DIR = _HERE.replace(os.sep, "/") + "/"


def main():
session = configure_logging(CASE_DIR)
try:
# 1. Set up assets.
jfh = JetFiringHistory.JetFiringHistory(CASE_DIR)

tv = TargetVehicle.TargetVehicle(CASE_DIR)
tv.set_stl()

lm = LogisticsModule.LogisticsModule(CASE_DIR)
# Define LM mass distribution properties.
lm.set_inertial_props(14000, 11, 2) # mass (kg), height (m), radius (m)
lm.set_thruster_config()
lm.set_thruster_metrics()
lm.assign_thruster_groups()

me = MissionEnvironment.MissionEnvironment(CASE_DIR)
study = PlumeStrikeEstimationStudy.PlumeStrikeEstimationStudy(me)
study.study_init(jfh, tv, lm)

# Uses the pre-generated 1D-approach JFH shipped with the case.
jfh.read_jfh()

# 2. Execute the RPOD plume-strike analysis.
study.graph_jfh()
study.jfh_plume_strikes()

session.finalize("successful")
except Exception:
session.finalize("failed")
raise
finally:
session.close()

print("Run complete. Runtime log:", session.log_path)


if __name__ == "__main__":
main()
19 changes: 19 additions & 0 deletions case/rpod/base_case/logging.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# PyRPOD logging configuration for the base_case RPOD plume-strike case.
#
# Read by pyrpod.logging_utils.configure_logging(case_dir) when this case is
# run (e.g. via run.py). See docs/logging.md and docs/logging.ini.example for
# the full schema and configuration precedence.
#
# base_case is the reference demonstration: default INFO logging to both the
# console and a runtime log file, with a progress record for every firing.

[logging]
enabled = true
console = true
file = true
level = INFO
progress_every_n_firings = 1
log_array_stats = true
log_performance = true
snapshot_config = true
checksum_inputs = true
65 changes: 65 additions & 0 deletions case/rpod/base_case/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""Demonstration driver for the base_case RPOD plume-strike case.

Mirrors tests/rpod/rpod_integration_test_01.py (minus the assertions) and wraps
the workflow in PyRPOD's operational logging. Run it directly from anywhere:

python case/rpod/base_case/run.py

Logging is configured from this case's logging.ini. A fresh runtime log is
written to:

case/rpod/base_case/results/logs/base_case_<timestamp>.log

along with a config.ini snapshot and SHA-256 checksums of every input asset.
"""
import os
import sys

_HERE = os.path.dirname(os.path.abspath(__file__))
_REPO_ROOT = os.path.abspath(os.path.join(_HERE, "..", "..", ".."))
if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT)

from pyrpod.logging_utils import configure_logging
from pyrpod.rpod import JetFiringHistory, PlumeStrikeEstimationStudy
from pyrpod.vehicle import LogisticsModule, TargetVehicle
from pyrpod.mission import MissionEnvironment

# PyRPOD's case_dir convention is a directory path with a trailing separator.
CASE_DIR = _HERE.replace(os.sep, "/") + "/"


def main():
session = configure_logging(CASE_DIR)
try:
# 1. Set up assets.
jfh = JetFiringHistory.JetFiringHistory(CASE_DIR)

tv = TargetVehicle.TargetVehicle(CASE_DIR)
tv.set_stl()

lm = LogisticsModule.LogisticsModule(CASE_DIR)
lm.set_thruster_config()

me = MissionEnvironment.MissionEnvironment(CASE_DIR)
study = PlumeStrikeEstimationStudy.PlumeStrikeEstimationStudy(me)
study.study_init(jfh, tv, lm)

jfh.read_jfh()

# 2. Execute the RPOD plume-strike analysis.
study.graph_jfh()
study.jfh_plume_strikes()

session.finalize("successful")
except Exception:
session.finalize("failed")
raise
finally:
session.close()

print("Run complete. Runtime log:", session.log_path)


if __name__ == "__main__":
main()
19 changes: 19 additions & 0 deletions case/rpod/hollow_cube/logging.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# PyRPOD logging configuration for the hollow_cube RPOD plume-strike case.
#
# Read by pyrpod.logging_utils.configure_logging(case_dir). See docs/logging.md
# and docs/logging.ini.example for the full schema.
#
# Demonstrates independent handler toggles: the console handler is disabled, so
# output goes only to the runtime log file under results/logs/. Useful for
# unattended/batch runs.

[logging]
enabled = true
console = false
file = true
level = DEBUG
progress_every_n_firings = 1
log_array_stats = true
log_performance = true
snapshot_config = true
checksum_inputs = true
60 changes: 60 additions & 0 deletions case/rpod/hollow_cube/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Demonstration driver for the hollow_cube RPOD plume-strike case.

Mirrors tests/rpod/rpod_integration_test_04.py (minus the assertions) and wraps
the workflow in PyRPOD's operational logging. Run it with:

python case/rpod/hollow_cube/run.py

This case's logging.ini disables console output, so records go only to the
runtime log file in case/rpod/hollow_cube/results/logs/.
"""
import os
import sys

_HERE = os.path.dirname(os.path.abspath(__file__))
_REPO_ROOT = os.path.abspath(os.path.join(_HERE, "..", "..", ".."))
if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT)

from pyrpod.logging_utils import configure_logging
from pyrpod.rpod import JetFiringHistory, PlumeStrikeEstimationStudy
from pyrpod.vehicle import TargetVehicle, VisitingVehicle
from pyrpod.mission import MissionEnvironment

CASE_DIR = _HERE.replace(os.sep, "/") + "/"


def main():
session = configure_logging(CASE_DIR)
try:
# 1. Set up assets.
jfh = JetFiringHistory.JetFiringHistory(CASE_DIR)
jfh.read_jfh()

tv = TargetVehicle.TargetVehicle(CASE_DIR)
tv.set_stl()

vv = VisitingVehicle.VisitingVehicle(CASE_DIR)
vv.set_stl()
vv.set_thruster_config()

me = MissionEnvironment.MissionEnvironment(CASE_DIR)
study = PlumeStrikeEstimationStudy.PlumeStrikeEstimationStudy(me)
study.study_init(jfh, tv, vv)

# 2. Execute the RPOD plume-strike analysis.
study.graph_jfh()
study.jfh_plume_strikes()

session.finalize("successful")
except Exception:
session.finalize("failed")
raise
finally:
session.close()

print("Run complete. Runtime log:", session.log_path)


if __name__ == "__main__":
main()
19 changes: 19 additions & 0 deletions case/rpod/koz/logging.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# PyRPOD logging configuration for the koz (keep-out-zone) RPOD case.
#
# Read by pyrpod.logging_utils.configure_logging(case_dir). See docs/logging.md
# and docs/logging.ini.example for the full schema.
#
# Demonstrates verbose diagnostics: level = DEBUG adds candidate/resolved asset
# paths, NumPy array summaries, and per-firing detail on top of the normal INFO
# workflow milestones. Handy when investigating a case.

[logging]
enabled = true
console = true
file = true
level = DEBUG
progress_every_n_firings = 1
log_array_stats = true
log_performance = true
snapshot_config = true
checksum_inputs = true
61 changes: 61 additions & 0 deletions case/rpod/koz/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Demonstration driver for the koz (keep-out-zone) RPOD plume-strike case.

Mirrors tests/rpod/rpod_integration_test_03.py (minus the assertions) and wraps
the workflow in PyRPOD's operational logging. Run it with:

python case/rpod/koz/run.py

This case's logging.ini sets level = DEBUG, so the runtime log in
case/rpod/koz/results/logs/ also includes candidate asset paths, array
summaries, and per-firing diagnostics.
"""
import os
import sys

_HERE = os.path.dirname(os.path.abspath(__file__))
_REPO_ROOT = os.path.abspath(os.path.join(_HERE, "..", "..", ".."))
if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT)

from pyrpod.logging_utils import configure_logging
from pyrpod.rpod import JetFiringHistory, PlumeStrikeEstimationStudy
from pyrpod.vehicle import LogisticsModule, TargetVehicle
from pyrpod.mission import MissionEnvironment

CASE_DIR = _HERE.replace(os.sep, "/") + "/"


def main():
session = configure_logging(CASE_DIR)
try:
# 1. Set up assets.
jfh = JetFiringHistory.JetFiringHistory(CASE_DIR)

tv = TargetVehicle.TargetVehicle(CASE_DIR)
tv.set_stl()

lm = LogisticsModule.LogisticsModule(CASE_DIR)
lm.set_thruster_config()

me = MissionEnvironment.MissionEnvironment(CASE_DIR)
study = PlumeStrikeEstimationStudy.PlumeStrikeEstimationStudy(me)
study.study_init(jfh, tv, lm)

jfh.read_jfh()

# 2. Execute the RPOD plume-strike analysis.
study.graph_jfh()
study.jfh_plume_strikes()

session.finalize("successful")
except Exception:
session.finalize("failed")
raise
finally:
session.close()

print("Run complete. Runtime log:", session.log_path)


if __name__ == "__main__":
main()
20 changes: 20 additions & 0 deletions case/rpod/multi_thrusters_square/logging.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# PyRPOD logging configuration for the multi_thrusters_square RPOD case.
#
# Read by pyrpod.logging_utils.configure_logging(case_dir). See docs/logging.md
# and docs/logging.ini.example for the full schema.
#
# This case enables plume kinetics (Simplified + Maxwellian), so the per-firing
# INFO progress records include the physical maxima (max_pressure_pa,
# max_shear_pa, max_heat_flux_w_m2, max_heat_flux_load_j_m2) in addition to
# geometry. Performance logging reports wall/CPU time and peak Python memory.

[logging]
enabled = true
console = true
file = true
level = INFO
progress_every_n_firings = 1
log_array_stats = true
log_performance = true
snapshot_config = true
checksum_inputs = true
Loading
Loading