def test_generate_instruction_candidates(asset: AssetFactory, boa: BOAFactory) -> None: """Since each sub-function is tested more thoroughly and directly this is only tested in a high-level way.""" states = KeyStore( keys=get_keys(AssetState), objects={ AssetStateFactory( asset=asset, start=boa.start - timedelta(hours=1), end=boa.end + timedelta(hours=1), available=True, charge=200, ) } ) instructions = KeyStore( keys=get_keys(Instruction), objects=[], ) output = generate_instruction_candidates( boa=boa, states=states, instructions=instructions, execution_time=datetime(2000, 1, 1), ) assert len(output) == 21
def get_prior_instruction_fixtures(asset: Asset) -> Dict: """Fixtures associated with the test_get_prior_instruction tests.""" past_2 = InstructionFactory(asset=asset, end=datetime(1999, 12, 31, 0)) past_1 = InstructionFactory(asset=asset, end=datetime(1999, 12, 31, 12)) future_1 = InstructionFactory(asset=asset, end=datetime(2000, 1, 1, 12)) future_2 = InstructionFactory(asset=asset, end=datetime(2000, 1, 2)) other_1 = InstructionFactory() other_2 = InstructionFactory() return { # 1: Single asset, instruction before time exists 1: { "asset": asset, "time": datetime(2000, 1, 1, 0, 0), "instructions": KeyStore( keys=get_keys(Instruction), objects=[past_2, past_1, future_1, future_2, other_1, other_2], ), "output": past_1 }, # 2: Single asset, instruction before time does not exist 2: { "asset": asset, "time": datetime(2000, 1, 1, 0, 0), "instructions": KeyStore( keys=get_keys(Instruction), objects=[future_1, future_2, other_1, other_2], ), "output": None }, # 3: Different asset only 3: { "asset": asset, "time": datetime(2000, 1, 1, 0, 0), "instructions": KeyStore( keys=get_keys(Instruction), objects=[other_1, other_2], ), "output": None }, }
def test_key_store__get(key_store: KeyStore): output = key_store.get(name="One") assert len(output) == 1 assert output[0].name == "One" assert key_store._cache[( "One", None, )] == output output = key_store.get(capacity=0) assert len(output) == 3 assert len(key_store._cache) == 2
def test_run_engine(assets: Tuple[Asset], ramp_rates: Dict, asset_mw: Dict[str, int]) -> None: boa = BOAFactory( mw=10, offer__price_mw_hr=10, offer__bmu__assets=tuple(assets), start=datetime(2020, 1, 1, 1), end=datetime(2020, 1, 1, 4), ) candidates = [ Candidate( asset=asset, boa=boa, mw=mw, ) for asset in assets for mw in [0, 5, 10] if abs(mw) <= asset.capacity ] rates = KeyStore( keys=get_keys(Rate), objects=[ RateFactory(asset=asset, **ramp_rates.get(asset.name, {})) for asset in assets ], ) solution = run_engine( boa=boa, rates=rates, candidates=candidates, ) assert len(solution.instructions) == len(asset_mw) for instruction in solution.instructions: assert asset_mw[instruction.asset.name] == instruction.mw
def key_store() -> KeyStore: return KeyStore(keys=["name", "capacity"], objects=[ AssetFactory(name="One", capacity=0), AssetFactory(name="Two", capacity=0), AssetFactory(name="Three", capacity=0), ])
def test_key_store__get_one_or_none(key_store: KeyStore): output = key_store.get_one_or_none(name="One") assert output is not None assert output.name == "One" assert key_store._cache[( "One", None, )] == [output]
def balance_a_bmu( input_filepath: str, output_filepath: Optional[str] = None, do_visualise: bool = False, ) -> Solution: data = load_input_data(filepath=input_filepath) rates = KeyStore(keys=get_keys(Rate), objects=data.rates) # Pre-solve candidates = generate_instruction_candidates( boa=data.boa, states=KeyStore(keys=get_keys(AssetState), objects=data.states), instructions=KeyStore(keys=get_keys(Instruction), objects=data.instructions), execution_time=data.parameters.execution_time, ) # Engine solution = run_engine( boa=data.boa, rates=rates, candidates=candidates, ) # Post-solve if output_filepath is not None: dump_solution(filepath=output_filepath, solution=solution) if do_visualise: visualise( boa=data.boa, rates=rates, candidates=KeyStore(keys=get_keys(Candidate), objects=candidates), instructions=KeyStore(keys=get_keys(Instruction), objects=solution.instructions), ) return solution
def get_items_for_period( items: KeyStore, start: datetime, end: datetime, **kwargs ) -> List: """Given a keystore of items and a period return a list of the items that overlap the period.""" return [ item for item in items.get(**kwargs) if date_range_overlap( start_1=start, end_1=end, start_2=item.start, end_2=item.end, ) > 0 ]
def get_item_at_time( items: KeyStore, time: datetime, nullable: bool = False, **kwargs ) -> Optional: """Given a keystore of items and a time, return the item at that time if it exists, otherwise return none.""" items_at_time = [ item for item in items.get(**kwargs) if item.start <= time <= item.end ] if len(items_at_time) == 0 and nullable: return None elif len(items_at_time) != 1: raise RuntimeError( f"Only expected one item for {kwargs} at {time} got {items_at_time}" ) return items_at_time[0]
def asset_can_be_assigned_to_boa_fixtures( asset: AssetFactory, boa: BOAFactory, previous_instruction: InstructionFactory, current_instruction: InstructionFactory, ) -> Dict[int, Dict[str, Any]]: keys = get_keys(AssetState) state_available = AssetStateFactory( asset=asset, start=datetime(2000, 1, 1), end=datetime(2000, 2, 1), available=True, ) return { # 1: Asset can be assigned, withOUT previous and current 1: { "boa": boa, "states": KeyStore( keys=keys, objects=[state_available], ), "output": True, }, # 2: Asset can be assigned, with previous and current 2: { "boa": boa, "states": KeyStore( keys=keys, objects=[state_available], ), "current_instruction": current_instruction, "prior_instruction": previous_instruction, "output": True, }, # 3: Asset does not respect availability 3: { "boa": boa, "states": KeyStore( keys=keys, objects=[ state_available, AssetStateFactory( asset=asset, start=datetime(2000, 1, 1), end=datetime(2000, 2, 1), available=False, ) ] ), "output": False, }, # 4: Asset does not respect min zero time 4: { "boa": boa, "states": KeyStore( keys=keys, objects=[state_available], ), "prior_instruction": InstructionFactory(end=datetime(2000, 1, 1, 9)), "output": False, }, # 5: Asset does not respect min non-zero time withOUT current instr 5: { "boa": BOAFactory( start=datetime(2000, 1, 1, 0, 0), end=datetime(2000, 1, 1, 0, 1), ), "states": KeyStore( keys=keys, objects=[state_available], ), "output": False, }, # 6: Asset does not respect min non-zero time with current instr 6: { "boa": BOAFactory( start=datetime(2000, 1, 1, 0, 1), end=datetime(2000, 1, 1, 0, 3), ), "states": KeyStore( keys=keys, objects=[state_available], ), "current_instruction": InstructionFactory( start=datetime(2000, 1, 1, 0, 0), end=datetime(2000, 1, 1, 0, 2), ), "output": False, }, }
end=datetime(2000, 1, 2, 1), type="Fruit") ITEM_THREE = Item(name="Carrot", start=datetime(2000, 1, 1), end=datetime(2000, 1, 1, 1), type="Vegetable") ITEM_FOUR = Item(name="Carrot", start=datetime(2000, 1, 3), end=datetime(2000, 1, 3, 1), type="Vegetable") TEST_KEY_STORE = KeyStore( keys=["name", "time", "type"], objects=[ ITEM_ONE, ITEM_TWO, ITEM_THREE, ITEM_FOUR, ], ) @pytest.mark.parametrize( "items, time, nullable, kwargs, expected_output", [ # No items at time, nullable ( TEST_KEY_STORE, datetime(2000, 1, 10), True, {},
def test_key_store__get_one_or_none__none(key_store: KeyStore): output = key_store.get_one_or_none(name="Other") assert key_store._cache == {} assert output is None