Source code for wolfgpu.simplesimu.durations

from typing import Union
from enum import Enum
from functools import total_ordering

from wolfgpu.utils import parse_duration_to_seconds


[docs] class SimulationDurationType(Enum): """ A type of duration. This was introduced to abstract the duration of a simulation. """ # Note that the way we put it, there's no way you can do # a simulation that has no planned finite time (it's time # step could be infinitely small though).
[docs] SECONDS=0
""" A duration expressed in seconds. """
[docs] STEPS=1
""" A duration expressed in steps. """
@total_ordering
[docs] class SimulationDuration: """ In the GPU simulator, we represent durations about the simulation either as number of simulation steps either as simulation time (in seconds). This class can represent both. """ def __init__(self, _type:SimulationDurationType, duration: Union[float, int]): assert isinstance(_type, SimulationDurationType) #assert duration > 0, f"A duration must be strictly positive. You gave {duration}." if _type == SimulationDurationType.STEPS: assert type(duration) is int, "Simulation total number of steps must be an integer"
[docs] self.type = _type
[docs] self.duration = duration # Expressed in seconds
def __str__(self): if self.type == SimulationDurationType.STEPS: if self.duration <= 1: return f"{self.duration} step" else: return f"{self.duration} steps" elif self.type == SimulationDurationType.SECONDS: return f"{self.duration:g} s."
[docs] def to_dict(self): return { "type": self.type.name, "duration": self.duration }
@classmethod
[docs] def from_dict(self, d): t = SimulationDurationType[d["type"]] match t: case SimulationDurationType.SECONDS: d = float(d["duration"]) case SimulationDurationType.STEPS: d = int(d["duration"]) case _: raise Exception(f"Unexpected duration type, you gave {self.type}") return SimulationDuration(t,d)
def __add__(self, value:"SimulationDuration"): assert self.type == value.type, "You are mixing different types of durations" return SimulationDuration( self.type, self.duration + value.duration) def __eq__(self, value: object) -> bool: return self.type == value.type and self.duration == value.duration def __lt__(self, value: "SimulationDuration") -> bool: if self.type == SimulationDurationType.SECONDS and value.type == SimulationDurationType.SECONDS or \ self.type == SimulationDurationType.STEPS and value.type == SimulationDurationType.STEPS: return self.duration < value.duration else: raise Exception(f"Can't compare SimulationDuration objects. {self.type} != {value.type}") @classmethod
[docs] def from_seconds(cls, seconds) -> "SimulationDuration": return SimulationDuration(SimulationDurationType.SECONDS, parse_duration_to_seconds(seconds))
@classmethod
[docs] def from_steps(cls, steps) -> "SimulationDuration": return SimulationDuration(SimulationDurationType.STEPS, int(steps))
@classmethod
[docs] def from_str(cls, d: str) -> "SimulationDuration": # An integer is a number of steps # Anything else (float or hms) is a duration try: steps = int(d) except ValueError: return cls.from_seconds(d) return cls.from_steps(steps)
@classmethod
[docs] def zero_like(cls, d) -> "SimulationDuration": return SimulationDuration(d.type, 0)
[docs] def is_zero(self): return self.duration == 0
[docs] class ReportFrequencyType(Enum): """ When specifying the report period, one can use two different units. """ # FIXME Can't we just use a simulationduration ? # These are the values used by Wolf
[docs] ITERATION = 1
""" Report period in iterations """
[docs] SIMULATION_TIME = 0
""" Report period in simulation time (seconds) """
[docs] class TimeStepStrategy(Enum): """ Describes the way the time step is computed on each simulation step. """
[docs] FIXED_TIME_STEP=1
""" The time step is constant over all simulation steps. """
[docs] OPTIMIZED_TIME_STEP=2
""" The time step is the maximum of the minimum time steps authorized by the Courant formula. """