@@ -852,6 +852,181 @@ def add_point_comparison(self,
852852definitions = Validator ()
853853
854854
855+ class SummaryVariable :
856+ """Class to hold metadata for summary variables"""
857+ def __init__ (self ):
858+ self .name = None
859+ self .model_variable = None
860+ self .long_name = None
861+ self .short_name = None
862+ self .units = None
863+ self .vertical_integration = False
864+ self .vertical_average = False
865+ self .horizontal_average = False
866+
867+ def __str__ (self ):
868+ attrs = vars (self )
869+ return '\n ' .join ("%s: %s" % item for item in attrs .items ())
870+
871+ def __repr__ (self ):
872+ attrs = vars (self )
873+ return '\n ' .join ("%s: %s" % item for item in attrs .items ())
874+
875+
876+ class Summary :
877+ """
878+ Summary class for defining variables to be summarized from model output.
879+
880+ This class allows you to specify how variables should be processed for summaries,
881+ including vertical integration, vertical averaging, horizontal averaging, etc.
882+ """
883+
884+ keys = []
885+
886+ def __delattr__ (self , name ):
887+ """Remove variable from keys list when deleted"""
888+ if name != "keys" :
889+ if name in self .keys :
890+ self .keys .remove (name )
891+ super ().__delattr__ (name )
892+
893+ def remove (self , name ):
894+ """Remove a variable from the summary definitions"""
895+ if name != "keys" :
896+ if name in self .keys :
897+ self .keys .remove (name )
898+ super ().__delattr__ (name )
899+
900+ def reset (self ):
901+ """Reset all summary definitions"""
902+ for key in self .keys :
903+ super ().__delattr__ (key )
904+ self .keys = []
905+
906+ def __setattr__ (self , name , value ):
907+ """Add variable name to keys list when set"""
908+ if name != "keys" :
909+ if name not in self .keys :
910+ self .keys .append (name )
911+ super ().__setattr__ (name , value )
912+
913+ def __getitem__ (self , key ):
914+ """Allow dictionary-style access to variables"""
915+ return getattr (self , key , None )
916+
917+ def add_summary (
918+ self ,
919+ name = None ,
920+ model_variable = None ,
921+ long_name = None ,
922+ short_name = None ,
923+ vertical_integration = False ,
924+ vertical_average = False ,
925+ depth_range = None ,
926+ climatology_years = None
927+ ):
928+ """
929+ Add a variable to be summarized from model output.
930+
931+ Parameters
932+ ----------
933+ name : str
934+ Name identifier for the variable. Must contain only letters and numbers.
935+ model_variable : str
936+ Name of the variable in the model output files.
937+ long_name : str, optional
938+ Long descriptive name for the variable.
939+ short_name : str, optional
940+ Short name for the variable. Defaults to name if not provided.
941+ vertical_integration : bool, default False
942+ Whether to vertically integrate the variable.
943+ vertical_average : bool, default False
944+ Whether to vertically average the variable.
945+
946+ Returns
947+ -------
948+ None
949+
950+ Raises
951+ ------
952+ ValueError
953+ If name is not provided, contains invalid characters, or if both
954+ vertical_integration and vertical_average are True.
955+
956+ Examples
957+ --------
958+ >>> summary = Summary()
959+ >>> summary.add_summary(
960+ ... name="temperature",
961+ ... model_variable="temp",
962+ ... long_name="Sea Water Temperature",
963+ ... vertical_average=True,
964+ ... )
965+ """
966+
967+ # climatology_years must be a list of two integers if provided
968+ if climatology_years is None :
969+ raise ValueError ("climatology_years must be provided as [start_year, end_year]" )
970+ if climatology_years is not None :
971+ if not isinstance (climatology_years , (list , tuple )):
972+ raise ValueError ("climatology_years must be a list or tuple of two integers [start_year, end_year]" )
973+ if len (climatology_years ) != 2 :
974+ raise ValueError ("climatology_years must contain exactly 2 values [start_year, end_year]" )
975+ try :
976+ start_year = int (climatology_years [0 ])
977+ end_year = int (climatology_years [1 ])
978+ if start_year > end_year :
979+ raise ValueError ("start_year must be less than end_year in climatology_years" )
980+ except (TypeError , ValueError ) as e :
981+ raise ValueError ("climatology_years values must be integers" )
982+
983+
984+ # Validate required parameters
985+ if name is None :
986+ raise ValueError ("Name must be provided" )
987+
988+ # Validate name contains only letters and numbers
989+ if not re .match ("^[A-Za-z0-9]+$" , name ):
990+ raise ValueError ("Name can only contain letters and numbers" )
991+
992+ if model_variable is None :
993+ raise ValueError ("Model variable must be provided" )
994+
995+ # Validate that both vertical operations are not True
996+ if vertical_integration and vertical_average :
997+ raise ValueError ("Cannot specify both vertical_integration and vertical_average" )
998+
999+
1000+ # Create variable if it doesn't exist, or get existing one
1001+ if getattr (self , name , None ) is None :
1002+ var = SummaryVariable ()
1003+ setattr (self , name , var )
1004+ else :
1005+ var = self [name ]
1006+
1007+ # Set attributes
1008+ var .name = name
1009+ var .model_variable = model_variable
1010+ var .climatology_years = climatology_years
1011+
1012+ # Set optional attributes with defaults
1013+ if long_name is not None :
1014+ var .long_name = long_name
1015+ else :
1016+ var .long_name = name if var .long_name is None else var .long_name
1017+
1018+ if short_name is not None :
1019+ var .short_name = short_name
1020+ else :
1021+ var .short_name = name if var .short_name is None else var .short_name
1022+
1023+ var .vertical_integration = vertical_integration
1024+ var .vertical_average = vertical_average
1025+
1026+
1027+ summaries = Summary ()
1028+
1029+
8551030def generate_mapping (ds ):
8561031 """
8571032 Generate mapping of model and observational variables
@@ -890,4 +1065,38 @@ def generate_mapping(ds):
8901065
8911066 return model_dict
8921067
1068+ def generate_mapping_summary (ds ):
1069+ """
1070+ Generate mapping of model and observational variables
1071+ """
1072+
1073+ model_dict = {}
1074+ try :
1075+ candidate_variables = summaries .keys
1076+ ds1 = nc .open_data (ds [0 ], checks = False )
1077+ ds_contents = ds1 .contents
1078+
1079+ ds_contents ["long_name" ] = [str (x ) for x in ds_contents ["long_name" ]]
8931080
1081+ ds_contents_top = ds_contents .query ("nlevels == 1" ).reset_index (drop = True )
1082+ n_levels = int (ds_contents .nlevels .max ())
1083+ if n_levels > session_info ["n_levels" ]:
1084+ session_info ["n_levels" ] = n_levels
1085+ # number of rows in ds_contents
1086+ if len (ds_contents ) == 0 :
1087+ ds_contents = ds_contents_top
1088+ except :
1089+ return model_dict
1090+
1091+ for vv in candidate_variables :
1092+ variables = summaries [vv ].model_variable .split ("+" )
1093+ include = True
1094+ for var in variables :
1095+ if var not in ds_contents .variable .values :
1096+ include = False
1097+ if include :
1098+ model_dict [vv ] = summaries [vv ].model_variable
1099+ n_levels = ds_contents .query ("variable in @variables" )["nlevels" ].max ()
1100+ continue
1101+
1102+ return model_dict
0 commit comments