1414VALID_EXAMPLE_FILES = glob .glob (os .path .join (DATA_DIR_VALID , '*.yaml' ))
1515INVALID_EXAMPLE_FILES = glob .glob (os .path .join (DATA_DIR_INVALID , '*.yaml' ))
1616
17-
1817# Mapping from identifier patterns or context to concrete class names
1918CHARACTERIZATION_TECHNIQUE_MAP = {
2019 'xray_source' : {
2423 'adsorbate_gas' : 'BET' ,
2524 'reducing_gas_composition' : 'TPR' ,
2625 'oxidizing_gas_composition' : 'TPO' ,
26+ 'excitation_laser_wavelength' : 'RamanSpectroscopy' ,
27+ 'minimum_wavenumber' : 'InfraredSpectroscopy' ,
28+ 'element_analyzed' : 'XRayAbsorptionSpectroscopy' ,
29+ 'nucleus' : 'NMRSpectroscopy' ,
30+ 'gun_type' : 'TransmissionElectronMicroscopy' ,
31+ 'image_resolution' : 'ScanningElectronMicroscopy' ,
32+ 'initial_temperature' : 'Thermogravimetry' ,
33+ 'combustion_temperature' : 'ElementalAnalysis' ,
34+ 'minimum_wavelength' : 'UVVisSpectroscopy' ,
35+ 'adsorption_gas' : 'DRIFTS' ,
36+ 'scan_rate' : 'CyclicVoltammetry' ,
37+ 'light_wavelength' : 'DynamicLightScattering' ,
38+ 'spray_voltage' : 'ESI_MS' ,
39+ 'excitation_wavelength' : 'PhotoluminescenceSpectroscopy' ,
40+ 'lifetime_fitting_model' : 'PhotoluminescenceLifetime' ,
41+ 'eluent' : 'SizeExclusionChromatography' ,
42+ 'gradient_program' : 'HPLC_MS' ,
43+ 'primary_energy' : 'EDX' ,
44+ 'electrode_configuration' : 'ConductivityMeasurement' ,
2745}
2846
2947PREPARATION_METHOD_MAP = {
3048 'impregnation_type' : 'Impregnation' ,
31- 'precipitating_agent' : 'CoPrecipitation' , # or DepositionPrecipitation
49+ 'precipitating_agent' : 'CoPrecipitation' ,
3250 'hydrolysis_ratio' : 'SolGel' ,
3351 'filling_volume' : 'Solvothermal' ,
3452 'plasma_type' : 'PlasmaAssisted' ,
3856 'sonication_power' : 'SonochemicalSynthesis' ,
3957 'flame_type' : 'FlameSprayPyrolysis' ,
4058 'ball_material' : 'MechanochemicalSynthesis' ,
41- 'synthesis_pressure' : 'Sublimation' ,
4259 'reaction_vessel' : 'MolecularSynthesis' ,
4360}
4461
5774 'band_path' : 'ElectronicStructure' ,
5875 'polarization_direction' : 'Ferroelectrics' ,
5976 'direct_indirect' : 'BandGap' ,
77+ 'material_composition' : 'DielectricTensors' ,
78+ 'force_constant_method' : 'PhononDispersion' ,
79+ 'fit_method' : 'EquationsOfState' ,
80+ 'ph_range' : 'AqueousStability' ,
81+ 'grain_boundary_plane' : 'GrainBoundaries' ,
82+ }
83+
84+ REACTOR_DESIGN_MAP = {
85+ 'gas_distributor_type' : 'FluidizedBedReactor' ,
86+ }
87+
88+ # Default classes for abstract types when no specific fields are found
89+ DEFAULT_CLASSES = {
90+ 'reactor_design_type' : 'FixedBedReactor' ,
91+ 'product_identification_method' : 'GCMS' ,
6092}
6193
6294
63- def infer_class_type (data : Dict [str , Any ], type_map : Dict [str , Any ]) -> str :
95+ def infer_class_type (data : Dict [str , Any ], type_map : Dict [str , Any ], default_class : str = None ) -> str :
6496 """Infer the concrete class type based on present fields."""
6597 for key , class_name in type_map .items ():
6698 if key in data :
@@ -71,7 +103,9 @@ def infer_class_type(data: Dict[str, Any], type_map: Dict[str, Any]) -> str:
71103 return cn
72104 else :
73105 return class_name
74- return None
106+
107+ # Return default class if no specific fields found
108+ return default_class
75109
76110
77111def instantiate_polymorphic_objects (data : Union [Dict , List ], parent_key : str = None ) -> Union [Dict , List ]:
@@ -82,32 +116,51 @@ def instantiate_polymorphic_objects(data: Union[Dict, List], parent_key: str = N
82116 if not isinstance (data , dict ):
83117 return data
84118
119+ # Check for explicit type hint (both 'type' and '@type' for LinkML compatibility)
120+ explicit_type = data .get ('type' ) or data .get ('@type' )
121+
85122 # Recursively process nested structures first
86123 result = {}
87124 for key , value in data .items ():
125+ if key in ('type' , '@type' ): # Skip the type hint fields
126+ continue
88127 result [key ] = instantiate_polymorphic_objects (value , key )
89128
90129 # Now handle polymorphic instantiation for specific keys
91130 if parent_key == 'characterization_technique' :
92- class_name = infer_class_type (result , CHARACTERIZATION_TECHNIQUE_MAP )
131+ class_name = explicit_type or infer_class_type (result , CHARACTERIZATION_TECHNIQUE_MAP )
93132 if class_name :
94133 cls = getattr (catcore , class_name )
95134 return cls (** result )
96135
97136 elif parent_key == 'preparation_method' :
98- class_name = infer_class_type (result , PREPARATION_METHOD_MAP )
137+ class_name = explicit_type or infer_class_type (result , PREPARATION_METHOD_MAP )
99138 if class_name :
100139 cls = getattr (catcore , class_name )
101140 return cls (** result )
102141
103142 elif parent_key == 'simulation_method' :
104- class_name = infer_class_type (result , SIMULATION_METHOD_MAP )
143+ class_name = explicit_type or infer_class_type (result , SIMULATION_METHOD_MAP )
105144 if class_name :
106145 cls = getattr (catcore , class_name )
107146 return cls (** result )
108147
109148 elif parent_key == 'calculated_property' :
110- class_name = infer_class_type (result , CALCULATED_PROPERTY_MAP )
149+ class_name = explicit_type or infer_class_type (result , CALCULATED_PROPERTY_MAP )
150+ if class_name :
151+ cls = getattr (catcore , class_name )
152+ return cls (** result )
153+
154+ elif parent_key == 'reactor_design_type' :
155+ class_name = explicit_type or infer_class_type (result , REACTOR_DESIGN_MAP ,
156+ DEFAULT_CLASSES .get ('reactor_design_type' ))
157+ if class_name :
158+ cls = getattr (catcore , class_name )
159+ return cls (** result )
160+
161+ elif parent_key == 'product_identification_method' :
162+ # For now, use GCMS as default if no specific fields found
163+ class_name = explicit_type or DEFAULT_CLASSES .get ('product_identification_method' )
111164 if class_name :
112165 cls = getattr (catcore , class_name )
113166 return cls (** result )
@@ -126,7 +179,18 @@ def test_valid_data_files(filepath):
126179 data_dict = yaml .safe_load (f )
127180
128181 # Handle polymorphic fields by instantiating concrete classes
129- for poly_field in ['characterization_technique' , 'preparation_method' , 'simulation_method' , 'calculated_property' ]:
182+ poly_fields = [
183+ 'characterization_technique' ,
184+ 'preparation_method' ,
185+ 'simulation_method' ,
186+ 'calculated_property' ,
187+ 'reactor_design_type' ,
188+ 'product_identification_method' ,
189+ 'operation_parameters' ,
190+ 'precursor'
191+ ]
192+
193+ for poly_field in poly_fields :
130194 if poly_field in data_dict and data_dict [poly_field ]:
131195 data_dict [poly_field ] = instantiate_polymorphic_objects (
132196 data_dict [poly_field ],
0 commit comments