11from collections import OrderedDict
2+ from copy import copy
23from enum import Enum
34from typing import Any , cast , List , Optional , Sequence , Union
45
@@ -263,7 +264,7 @@ def __init__(
263264 self , name : str , ports : Optional [Ports ] = None ,
264265 description : str = '' ,
265266 supported_settings : Optional [SupportedSettings ] = None ,
266- components : Optional [List [Component ]] = None ,
267+ components : Optional [Sequence [Component ]] = None ,
267268 conduits : Optional [Sequence [AnyConduit ]] = None ) -> None :
268269 """Create a Model.
269270
@@ -278,9 +279,19 @@ def __init__(
278279 super ().__init__ (name , ports , description , supported_settings )
279280
280281 if components is None :
281- self .components = []
282+ self .components = {}
282283 else :
283- self .components = components
284+ for i1 , c1 in enumerate (components ):
285+ num_conflicts = 0
286+ for i2 in range (i1 - 1 ):
287+ if c1 .name == components [i2 ].name :
288+ num_conflicts += 1
289+ if num_conflicts > 0 :
290+ raise ValueError (
291+ f'There are { num_conflicts + 1 } components named'
292+ f' { c1 .name } .' )
293+
294+ self .components = {copy (c .name ): c for c in components }
284295
285296 self .conduits = list () # type: List[Conduit]
286297 if conduits :
@@ -294,16 +305,13 @@ def check_consistent(self) -> List[str]:
294305 """Check that the model is internally consistent.
295306
296307 This checks:
297- - that no two components have the same name
298308 - that every conduit is connected to two existing ports on existing
299309 components, or on the model itself.
300310
301311 Returns a list of errors, or an empty list if none were found.
302312 """
303313 errors : List [str ] = list ()
304314
305- errors .extend (self ._check_component_name_conflicts ())
306-
307315 model_receiving_ports = self .ports .receiving_port_names ()
308316 model_sending_ports = self .ports .sending_port_names ()
309317
@@ -313,24 +321,6 @@ def check_consistent(self) -> List[str]:
313321
314322 return errors
315323
316- def _check_component_name_conflicts (self ) -> List [str ]:
317- """Check that no two components have the same name.
318-
319- Returns a list of errors.
320- """
321- errors = list ()
322-
323- for i1 , c1 in enumerate (self .components ):
324- num_conflicts = 0
325- for i2 in range (i1 - 1 ):
326- if c1 .name == self .components [i2 ].name :
327- num_conflicts += 1
328- if num_conflicts > 0 :
329- errors .append (
330- f'There are { num_conflicts + 1 } components named { c1 .name } .' )
331-
332- return errors
333-
334324 def _check_sending_side (
335325 self , conduit : Conduit , model_receiving_ports : List [Identifier ]
336326 ) -> List [str ]:
@@ -344,23 +334,20 @@ def _check_sending_side(
344334 f' { conduit .sending_port ()} that does not exist.' )
345335 else :
346336 # from component
347- snd_cmp = [
348- c for c in self .components
349- if c .name == conduit .sending_component ()]
350- if not snd_cmp :
337+ if conduit .sending_component () not in self .components :
351338 errors .append (
352339 f'Conduit { conduit } refers to a component named'
353340 f' { conduit .sending_component ()} , which is not present in'
354341 ' the model.' )
355342 else :
356- if len ( snd_cmp ) == 1 :
357- cmp_sending_ports = snd_cmp [ 0 ] .ports .sending_port_names ()
358- if conduit .sending_port () not in cmp_sending_ports :
359- errors .append (
360- f'Conduit { conduit } refers to a sending port named'
361- f' { conduit .sending_port ()} , which is not present'
362- f' on sending component { snd_cmp [ 0 ] .name } , or is not'
363- ' an O_I or O_F port.' )
343+ snd_cmp = self . components [ conduit . sending_component ()]
344+ cmp_sending_ports = snd_cmp .ports .sending_port_names ()
345+ if conduit .sending_port () not in cmp_sending_ports :
346+ errors .append (
347+ f'Conduit { conduit } refers to a sending port named'
348+ f' { conduit .sending_port ()} , which is not present on '
349+ f' sending component { snd_cmp .name } , or is not an O_I or '
350+ ' O_F port.' )
364351 return errors
365352
366353 def _check_receiving_side (
@@ -376,26 +363,23 @@ def _check_receiving_side(
376363 f' { conduit .receiving_port ()} that does not exist.' )
377364 else :
378365 # to component
379- rcvng_cmp = [
380- c for c in self .components
381- if c .name == conduit .receiving_component ()]
382- if not rcvng_cmp :
366+ if conduit .receiving_component () not in self .components :
383367 errors .append (
384368 f'Conduit { conduit } refers to a component named'
385369 f' { conduit .receiving_component ()} , which is not present in'
386370 ' the model.' )
387371 else :
388- if len ( rcvng_cmp ) == 1 :
389- rcvr_recv_ports = (
390- rcvng_cmp [ 0 ] .ports .receiving_port_names () +
391- ['muscle_settings_in' ])
392-
393- if conduit .receiving_port () not in rcvr_recv_ports :
394- errors .append (
395- f'Conduit { conduit } refers to a receiving port named'
396- f' { conduit .receiving_port ()} , which is not present'
397- f' on receiving component { rcvng_cmp [ 0 ] .name } , or is'
398- ' not an F_INIT or S port.' )
372+ rcvng_cmp = self . components [ conduit . receiving_component ()]
373+ rcvr_recv_ports = (
374+ rcvng_cmp .ports .receiving_port_names () +
375+ ['muscle_settings_in' ])
376+
377+ if conduit .receiving_port () not in rcvr_recv_ports :
378+ errors .append (
379+ f'Conduit { conduit } refers to a receiving port named'
380+ f' { conduit .receiving_port ()} , which is not present on '
381+ f' receiving component { rcvng_cmp .name } , or is not an '
382+ ' F_INIT or S port.' )
399383 return errors
400384
401385 def _conduits_for_export (self ) -> List [AnyConduit ]:
@@ -439,7 +423,7 @@ def _yatiml_savorize(cls, node: yatiml.Node) -> None:
439423
440424 @classmethod
441425 def _yatiml_sweeten (cls , node : yatiml .Node ) -> None :
442- node .seq_attribute_to_map ('components' , 'name' , )
426+ node .index_attribute_to_map ('components' , 'name' )
443427
444428 if len (node .get_attribute ('conduits' ).seq_items ()) == 0 :
445429 node .remove_attribute ('conduits' )
0 commit comments