Reference Guide  2.5.0
lfric_symbol_table.py
1 # -----------------------------------------------------------------------------
2 # BSD 3-Clause License
3 #
4 # Copyright (c) 2017-2024, Science and Technology Facilities Council.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are met:
9 #
10 # * Redistributions of source code must retain the above copyright notice, this
11 # list of conditions and the following disclaimer.
12 #
13 # * Redistributions in binary form must reproduce the above copyright notice,
14 # this list of conditions and the following disclaimer in the documentation
15 # and/or other materials provided with the distribution.
16 #
17 # * Neither the name of the copyright holder nor the names of its
18 # contributors may be used to endorse or promote products derived from
19 # this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 # POSSIBILITY OF SUCH DAMAGE.
33 # -----------------------------------------------------------------------------
34 # Authors: R. W. Ford, A. R. Porter, S. Siso and N. Nobre, STFC Daresbury Lab
35 # I. Kavcic, Met Office
36 # J. Henrichs, Bureau of Meteorology
37 # Modified: O.Brunt, Met Office
38 # -----------------------------------------------------------------------------
39 
40 ''' This module contains the LFRic-specific SymbolTable implementation.
41 It provides convenience functions to create often used symbols.
42 '''
43 
44 from psyclone.configuration import Config
45 from psyclone.domain.lfric import LFRicConstants
46 # Avoid circular import:
47 from psyclone.domain.lfric.lfric_types import LFRicTypes
48 
49 from psyclone.psyir.symbols import (ArrayType, ContainerSymbol, DataSymbol,
50  ImportInterface, INTEGER_TYPE, ScalarType,
51  Symbol, SymbolTable)
52 
53 
54 class LFRicSymbolTable(SymbolTable):
55  # pylint: disable=abstract-method
56  '''
57  Sub-classes SymbolTable to provide a LFRic-specific implementation.
58 
59  :param node: reference to the Schedule or Container to which this \
60  symbol table belongs.
61  :type node: :py:class:`psyclone.psyir.nodes.Schedule`, \
62  :py:class:`psyclone.psyir.nodes.Container` or NoneType
63  :param default_visibility: optional default visibility value for this \
64  symbol table, if not provided it defaults to PUBLIC visibility.
65  :type default_visibillity: \
66  :py:class:`psyclone.psyir.symbols.Symbol.Visibility`
67 
68  '''
69 
70  # The container symbol for all precision variables
71  _constants_mod = None
72  # A mapping of 'i_def' etc. to the corresponding DataSymbole
73  _precision_map = {}
74 
75  def __init__(self, node=None, default_visibility=Symbol.Visibility.PUBLIC):
76  super().__init__(node, default_visibility)
77  # First time an instance of this is created, define
78  # the precision mapping.
79  if not LFRicSymbolTable._precision_map:
80  const = LFRicConstants()
81  mod_name = const.UTILITIES_MOD_MAP["constants"]["module"]
82  LFRicSymbolTable._constants_mod = ContainerSymbol(mod_name)
83 
84  api_config = Config.get().api_conf("dynamo0.3")
85  for precision in api_config.precision_map:
86  LFRicSymbolTable._precision_map[precision] = \
87  DataSymbol(precision, INTEGER_TYPE,
88  interface=ImportInterface(self._constants_mod_constants_mod))
89 
90  def find_or_create_integer_symbol(self, name, tag=None):
91  '''This function returns a symbol for an integer reference. If a
92  tag is specified, it will be used to search for an existing symbol,
93  otherwise the name will be used. If the symbol should not already
94  exist in the symbol table, it will be returned, otherwise a new
95  symbol will be created.
96 
97  :param str name: name of the integer variable to declare.
98  :param tag: optional tag of the integer variable to declare.
99  :type tag: Optional[str]
100 
101  :returns: the symbol for the variable.
102  :rtype: :py:class:`psyclone.psyir.symbols.Symbol`
103 
104  :raises TypeError: TypeError if the symbol exists but is not \
105  a DataSymbol.
106  :raises TypeError: TypeError if the symbol exists and is a \
107  DataSymbol, but not an Integer.
108 
109  '''
110  if tag:
111  try:
112  sym = self.lookup_with_tag(tag)
113  except KeyError:
114  sym = None
115  else:
116  try:
117  sym = self.lookup(name)
118  except KeyError:
119  sym = None
120 
121  datatype = LFRicTypes("LFRicIntegerScalarDataType")()
122  if sym is None:
123  # Create a DataSymbol for this kernel argument.
124  sym = self.new_symbol(name, tag=tag,
125  symbol_type=DataSymbol,
126  datatype=datatype)
127  else:
128  # The symbol already exists, check that is the right type:
129  if not isinstance(sym, DataSymbol):
130  raise TypeError(f"Symbol '{sym.name}' already exists, but is "
131  f"not a DataSymbol, but '{type(sym)}'.")
132  if sym.datatype != datatype:
133  raise TypeError(f"Symbol '{sym.name}' already exists, but is "
134  f"not an integer, but '{sym.datatype}'.")
135  return sym
136 
137  # ------------------------------------------------------------------------
138  def find_or_create_array(self, array_name, num_dimensions, intrinsic_type,
139  tag=None):
140  '''This function returns a symbol for an ArrayReference. If the
141  symbol does not exist, it is created. If a new array symbol is
142  created, it gets the DEFERRED attribute, which in Fortran means
143  it will be declared as an allocatable array.
144 
145  :param str array_name: the name and tag of the array.
146  :param int num_dimensions: the number of dimensions of this array.
147  :param intrinsic_type: the intrinsic type of the array.
148  :type intrinsic_type: \
149  :py:class:`psyclone.psyir.symbols.datatypes.ScalarType.Intrinsic`
150  :param tag: optional tag to be used in searching and defining.
151  :type tag: Optional[str]
152 
153  :returns: the requested symbol
154  :rtype: :py:class:`psyclone.psyir.symbols.Symbol`
155 
156  :raises TypeError: if the symbol exists, but is not a DataSymbol, \
157  or not an Array, or has different number of dimensions.
158 
159  '''
160  if intrinsic_type == ScalarType.Intrinsic.REAL:
161  datatype = LFRicTypes("LFRicRealScalarDataType")()
162  elif intrinsic_type == ScalarType.Intrinsic.INTEGER:
163  datatype = LFRicTypes("LFRicIntegerScalarDataType")()
164  elif intrinsic_type == ScalarType.Intrinsic.BOOLEAN:
165  datatype = LFRicTypes("LFRicLogicalScalarDataType")()
166  else:
167  raise TypeError(f"Unsupported data type "
168  f"'{intrinsic_type}' in "
169  f"find_or_create_array")
170 
171  try:
172  if tag:
173  sym = self.lookup_with_tag(tag)
174  else:
175  sym = self.lookup(array_name)
176  except KeyError:
177  # pylint: disable=raise-missing-from
178  # Create a DataSymbol for this kernel argument.
179  array_type = ArrayType(datatype,
180  [ArrayType.Extent.DEFERRED]*num_dimensions)
181 
182  sym = self.new_symbol(array_name, tag=tag,
183  symbol_type=DataSymbol,
184  datatype=array_type)
185  return sym
186 
187  # Symbol already exists, check consistency:
188  if not isinstance(sym, DataSymbol):
189  raise TypeError(f"Symbol '{sym.name}' already exists, but is "
190  f"not a DataSymbol, but '{type(sym)}'.")
191 
192  if not isinstance(sym.datatype, ArrayType):
193  raise TypeError(f"Symbol '{sym.name}' already exists, but is "
194  f"not an ArraySymbol, but "
195  f"'{type(sym.datatype)}'.")
196 
197  if sym.datatype.datatype != datatype:
198  raise TypeError(f"Symbol '{sym.name}' already exists, but is "
199  f"not of type '{intrinsic_type}', but "
200  f"'{type(sym.datatype.datatype)}'.")
201 
202  if len(sym.shape) != num_dimensions:
203  raise TypeError(f"Array '{sym.name}' already exists, but has "
204  f"{len(sym.shape)} dimensions, not "
205  f"{num_dimensions}.")
206 
207  return sym
208 
209  # ------------------------------------------------------------------------
210  def add_lfric_precision_symbol(self, name):
211  '''
212  If the named LFRic precision symbol is not already in the table then
213  add it. Also ensure that the Container symbol from which it is
214  imported is in the table.
215 
216  :param str name: name of the LFRic precision symbol to add to table.
217 
218  :returns: the specified LFRic precision symbol.
219  :rtype: :py:class:`psyclone.psyir.symbols.DataSymbol`
220 
221  :raises ValueError: if the supplied name is not a recognised LFRic \
222  precision variable.
223  :raises ValueError: if a symbol with the same name is already in the \
224  table but is not imported from the correct container.
225 
226  '''
227  api_config = Config.get().api_conf("dynamo0.3")
228  if name not in api_config.precision_map.keys():
229  raise ValueError(f"'{name}' is not a recognised LFRic precision.")
230  sym = LFRicSymbolTable._precision_map[name]
231 
232  if name in self:
233  # Sanity check that the existing symbol is the right one.
234  existing_sym = self.lookup(name)
235  if (not isinstance(existing_sym.interface, ImportInterface) or
236  existing_sym.interface.container_symbol.name !=
237  self._constants_mod_constants_mod.name):
238  raise ValueError(
239  f"Precision symbol '{name}' already exists in the "
240  f"supplied symbol table but is not imported from the "
241  f"LFRic constants module ({self._constants_mod.name}).")
242  return existing_sym
243 
244  # pylint: disable=undefined-variable
245  if self._constants_mod_constants_mod.name not in self:
246  self.add(self._constants_mod_constants_mod)
247  self.add(sym)
248  return sym
def find_or_create_array(self, array_name, num_dimensions, intrinsic_type, tag=None)