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
1 change: 1 addition & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
f728f7fc0e523b6bf33592dd41661cb331efa924
e5d8bb62af96a0900aa77b348ac076033d38c0fc
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGELOG

## v0.13.1 - 7 January 2026

- Updates the project capacity check for a true 6 decimal precision check.
- Breaks apart all long error messages so they are defined outside of the error call.

## v0.13 - 23 December 2025

### Default Data Now Available
Expand Down
2 changes: 1 addition & 1 deletion wombat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
from wombat.core.library import create_library_structure, load_yaml


__version__ = "0.13"
__version__ = "0.13.1"
53 changes: 30 additions & 23 deletions wombat/core/data_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,17 +372,17 @@ def annual_date_range(
"""
# Check the year bounds
if end_year < start_year:
raise ValueError(
f"The end_year ({start_year}) is later than the start_year ({end_year})."
)
msg = f"The end_year ({start_year}) is later than the start_year ({end_year})."
raise ValueError(msg)

# Check the month, date combination bounds
start = datetime.datetime(2022, start_month, start_day)
end = datetime.datetime(2022, end_month, end_day)
if end < start:
raise ValueError(
msg = (
f"The starting month/day combination: {start}, is after the ending: {end}."
)
raise ValueError(msg)

# Create a list of arrays of date ranges for each year
start = datetime.datetime(1, start_month, start_day)
Expand Down Expand Up @@ -470,22 +470,25 @@ def check_start_stop_dates(
if value is None:
if start_date is None:
return
raise ValueError(
msg = (
"A starting date was provided, but no ending date was provided"
f" for `{attribute.name}`."
)
raise ValueError(msg)

if start_date is None:
raise ValueError(
msg = (
"An ending date was provided, but no starting date was provided"
f" for `{start_name}`."
)
raise ValueError(msg)

if start_date == value:
raise ValueError(
msg = (
f"Starting date (`{start_name}`={start_date} and ending date"
f" (`{attribute.name}`={value}) cannot be the same date."
)
raise ValueError(msg)


def convert_maintenance_list(value: list[dict], self_) -> list[Maintenance]:
Expand Down Expand Up @@ -547,10 +550,11 @@ def validate_0_1_inclusive(
The input value for `speed_reduction_factor`.
"""
if value < 0 or value > 1:
raise ValueError(
msg = (
f"Input for {attribute.name} must be between 0 and 1, inclusive, not:"
f" {value=}."
)
raise ValueError(msg)


def to_datetime(value: str | datetime.datetime) -> datetime.datetime:
Expand Down Expand Up @@ -627,10 +631,11 @@ def from_dict(cls, data: dict):
]
undefined = sorted(set(required_inputs) - set(kwargs))
if undefined:
raise AttributeError(
msg = (
f"The class defintion for {cls.__name__} is missing the following"
f" inputs: {undefined}"
)
raise AttributeError(msg)
return cls(**kwargs)


Expand Down Expand Up @@ -1216,9 +1221,8 @@ def _compare_dates(
original_start = getattr(self, f"{which}_start")
original_end = getattr(self, f"{which}_end")
else:
raise ValueError(
"`which` must be one of 'reduced_speed' or 'non_operational'."
)
msg = "`which` must be one of 'reduced_speed' or 'non_operational'."
raise ValueError(msg)

if original_start is not None:
if new_start is not None:
Expand Down Expand Up @@ -1284,16 +1288,16 @@ def set_non_operational_dates(

# Check that the input year range is valid
if not isinstance(start_year, int):
raise ValueError(
f"Input to `start_year`: {start_year}, must be an integer."
)
msg = f"Input to `start_year`: {start_year}, must be an integer."
raise ValueError(msg)
if not isinstance(end_year, int):
raise ValueError(f"Input to `end_year`: {end_year}, must be an integer.")
if end_year < start_year:
raise ValueError(
msg = (
"`start_year`: {start_year}, must less than or equal to the"
f" `end_year`: {end_year}"
)
raise ValueError(msg)

# Create the date range
dates = annualized_date_range(
Expand Down Expand Up @@ -1357,16 +1361,16 @@ def set_reduced_speed_parameters(

# Check that the input year range is valid
if not isinstance(start_year, int):
raise ValueError(
f"Input to `start_year`: {start_year}, must be an integer."
)
msg = f"Input to `start_year`: {start_year}, must be an integer."
raise ValueError(msg)
if not isinstance(end_year, int):
raise ValueError(f"Input to `end_year`: {end_year}, must be an integer.")
if end_year < start_year:
raise ValueError(
msg = (
"`start_year`: {start_year}, must less than or equal to the"
f" `end_year`: {end_year}"
)
raise ValueError(msg)

# Create the date range
dates = annualized_date_range(
Expand Down Expand Up @@ -1770,16 +1774,18 @@ def _validate_threshold(
"""Ensure a valid threshold is provided for a given ``strategy``."""
if self.strategy == "downtime":
if value <= 0 or value >= 1:
raise ValueError(
msg = (
"Downtime-based strategies must have a ``strategy_threshold``",
"between 0 and 1, non-inclusive!",
)
raise ValueError(msg)
if self.strategy == "requests":
if value <= 0:
raise ValueError(
msg = (
"Requests-based strategies must have a ``strategy_threshold``",
"greater than 0!",
)
raise ValueError(msg)

def __attrs_post_init__(self) -> None:
"""Post-initialization hook."""
Expand Down Expand Up @@ -1865,10 +1871,11 @@ def __attrs_post_init__(self):
self, "strategy", clean_string_input(self.data_dict["strategy"])
)
if self.strategy not in VALID_STRATEGIES:
raise ValueError(
msg = (
f"ServiceEquipment strategy should be one of {VALID_STRATEGIES};"
f" input: {self.strategy}."
)
raise ValueError(msg)

def determine_type(
self,
Expand Down
19 changes: 10 additions & 9 deletions wombat/core/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,8 @@ def __init__(
if not 0 <= self.workday_end <= 24:
raise ValueError("workday_end must be a valid 24hr time.")
if self.workday_end <= self.workday_start:
raise ValueError(
"Work shifts must end after they start ({self.workday_start}hrs)."
)
msg = "Work shifts must end after they start ({self.workday_start}hrs)."
raise ValueError(msg)

self.port_distance = port_distance
self.weather = self._weather_setup(weather_file, start_year, end_year)
Expand Down Expand Up @@ -491,10 +490,11 @@ def _weather_setup(
if start_year is None:
pass
elif start_year > self.end_year:
raise ValueError(
msg = (
f"'start_year' ({start_year}) occurs after the last available year"
f" in the weather data (range: {self.end_year})"
)
raise ValueError(msg)
else:
# Filter for the provided, validated starting year and update the attribute
weather = (
Expand All @@ -508,16 +508,18 @@ def _weather_setup(
if end_year is None:
pass
elif start_year is None and end_year < self.start_year:
raise ValueError(
msg = (
f"The provided 'end_year' ({end_year}) is before the start_year"
f" ({self.start_year})"
)
raise ValueError(msg)
elif start_year is not None:
if end_year < start_year:
raise ValueError(
msg = (
f"The provided 'end_year' ({end_year}) is before the start_year"
f" ({start_year})"
)
raise ValueError(msg)
else:
# Filter for the provided, validated ending year and update
weather = weather.filter(pl.col("datetime").dt.year() <= end_year)
Expand Down Expand Up @@ -647,9 +649,8 @@ def log_action(
"""
valid_locations = ("site", "system", "port", "enroute", "na")
if location not in valid_locations:
raise ValueError(
f"Event logging `location` must be one of: {valid_locations}"
)
msg = f"Event logging `location` must be one of: {valid_locations}"
raise ValueError(msg)
total_labor_cost = hourly_labor_cost + salary_labor_cost
total_cost = total_labor_cost + equipment_cost + materials_cost
now = self.simulation_time
Expand Down
5 changes: 2 additions & 3 deletions wombat/core/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,8 @@ def _check_working_hours(self, which: str) -> None:
start = self.port.settings.workday_start
end = self.port.settings.workday_end
else:
raise ValueError(
"Can only set the workday settings from a 'port' or 'env'."
)
msg = "Can only set the workday settings from a 'port' or 'env'."
raise ValueError(msg)

self.settings._set_environment_shift(
*check_working_hours(
Expand Down
36 changes: 16 additions & 20 deletions wombat/core/post_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,9 +422,8 @@ def time_based_availability(self, frequency: str, by: str) -> pd.DataFrame:

by = by.lower().strip()
if by not in ("windfarm", "turbine", "electrolyzer"):
raise ValueError(
'`by` must be one of "windfarm", "turbine", or "electrolyzer".'
)
msg = '`by` must be one of "windfarm", "turbine", or "electrolyzer".'
raise ValueError(msg)

by_windfarm = by == "windfarm"
by_electrolyzer = by == "electrolyzer"
Expand Down Expand Up @@ -498,9 +497,8 @@ def production_based_availability(self, frequency: str, by: str) -> pd.DataFrame

by = by.lower().strip()
if by not in ("windfarm", "turbine", "electrolyzer"):
raise ValueError(
'`by` must be one of "windfarm", "turbine", or "electrolyzer".'
)
msg = '`by` must be one of "windfarm", "turbine", or "electrolyzer".'
raise ValueError(msg)

by_windfarm = by == "windfarm"
by_electrolyzer = by == "electrolyzer"
Expand Down Expand Up @@ -592,9 +590,8 @@ def capacity_factor(self, which: str, frequency: str, by: str) -> pd.DataFrame:

by = by.lower().strip()
if by not in ("windfarm", "turbine", "electrolyzer"):
raise ValueError(
'`by` must be one of "windfarm", "turbine", or "electrolyzer".'
)
msg = '`by` must be one of "windfarm", "turbine", or "electrolyzer".'
raise ValueError(msg)

by_windfarm = by == "windfarm"
by_electrolyzer = by == "electrolyzer"
Expand Down Expand Up @@ -665,9 +662,8 @@ def task_completion_rate(self, which: str, frequency: str) -> float | pd.DataFra
"""
which = which.lower().strip()
if which not in ("scheduled", "unscheduled", "both"):
raise ValueError(
'``which`` must be one of "scheduled", "unscheduled", or "both".'
)
msg = '``which`` must be one of "scheduled", "unscheduled", or "both".'
raise ValueError(msg)

frequency = _check_frequency(frequency, which="all")

Expand Down Expand Up @@ -972,10 +968,11 @@ def vessel_crew_hours_at_sea(
raise ValueError("``by_equipment`` must be one of ``True`` or ``False``")

if not isinstance(vessel_crew_assumption, dict):
raise ValueError(
msg = (
"`vessel_crew_assumption` must be a dictionary of vessel name (keys)"
" and number of crew (values)"
)
raise ValueError(msg)

# Filter by the at sea indicators and required columns
at_sea = self.events
Expand Down Expand Up @@ -1593,9 +1590,8 @@ def emissions(
if missing := set(self.service_equipment_names).difference(
[*emissions_factors]
):
raise KeyError(
f"`emissions_factors` is missing the following keys: {missing}"
)
msg = f"`emissions_factors` is missing the following keys: {missing}"
raise KeyError(msg)

valid_categories = ("transit", "maneuvering", "idle at port", "idle at site")
emissions_categories = list(
Expand All @@ -1606,10 +1602,11 @@ def emissions(
len(set(valid_categories).difference(emissions_input.keys())) > 0
or len(set(emissions_input.values())) > 1
):
raise KeyError(
msg = (
"Each servicing equipment's emissions factors must have inputs for:"
f"{valid_categories}"
)
raise KeyError(msg)

# Create the agent/duration subset
equipment_usage = (
Expand Down Expand Up @@ -1909,9 +1906,8 @@ def project_fixed_costs(self, frequency: str, resolution: str) -> pd.DataFrame:

resolution = resolution.lower().strip()
if resolution not in ("low", "medium", "high"):
raise ValueError(
'``resolution`` must be one of "low", "medium", or "high".'
)
msg = '``resolution`` must be one of "low", "medium", or "high".'
raise ValueError(msg)

# Get the appropriate values and convert to the currency base
keys = self.fixed_costs.resolution[resolution]
Expand Down
13 changes: 6 additions & 7 deletions wombat/core/repair_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -525,9 +525,8 @@ def invalidate_system(
system.servicing_queue = self.env.event()
self.invalid_systems.append(system.id)
else:
raise RuntimeError(
f"{self.env.simulation_time} {system.id} already being serviced"
)
msg = f"{self.env.simulation_time} {system.id} already being serviced"
raise RuntimeError(msg)
if tow:
self.systems_in_tow.append(system.id)
_ = self.systems_waiting_for_tow.pop(
Expand Down Expand Up @@ -577,9 +576,8 @@ def interrupt_system(
system.servicing = self.env.event()
self.reset_subassembly_processes(system, subassembly_full_reset)
else:
raise RuntimeError(
f"{self.env.simulation_time} {system.id} already being serviced"
)
msg = f"{self.env.simulation_time} {system.id} already being serviced"
raise RuntimeError(msg)

def register_repair(self, repair: RepairRequest) -> Generator:
"""Registers the repair as complete with the repair managiner.
Expand Down Expand Up @@ -615,10 +613,11 @@ def enable_requests_for_system(
Set to True if this is for a tow-to-port request.
"""
if system.servicing.triggered:
raise RuntimeError(
msg = (
f"{self.env.simulation_time} Repairs were already completed"
f" at {system.id}"
)
raise RuntimeError(msg)
_ = self.invalid_systems.pop(self.invalid_systems.index(system.id))
if tow:
_ = self.systems_in_tow.pop(self.systems_in_tow.index(system.id))
Expand Down
Loading