Compare commits

..

6 Commits

Author SHA256 Message Date
0a879cbfe9 removes debug line, writes json to file instead (path: output/) 2026-02-13 11:49:59 +01:00
f60b58f2f2 ignores output of main.py (output/*.json) 2026-02-13 11:49:13 +01:00
6f618b2340 adds comments 2026-02-13 01:05:32 +01:00
38940995b5 completes the dataset with instruments_used (in a way...)
only lacks units of measurement, then I'll be ready for conversion
2026-02-13 00:59:22 +01:00
f686ea65b1 adds get_instruments method to Layer class
get_instruments returns a dictionary with the names of every system used
during the deposition
unfortunately, NeXus standard allows for a single value of all three
keys per every sample - not every layer
this means that every layer has its own data for laser, rheed system and
depo chamber which IDEALLY is the same for every layer, but in practice
they COULD be different and I still don't know how to deal with this
2026-02-13 00:32:31 +01:00
23bfdefd30 adds all the remaining layer data
only lacks the instrument_used data and units of measurement
NOTE: units of measurement are hard to collect, but could be assumed
considering our instruments are standard
2026-02-13 00:18:07 +01:00
4 changed files with 85 additions and 5 deletions

5
.gitignore vendored
View File

@@ -1,6 +1,9 @@
# ignora log di h5tojson e jsontoh5
# ignores logs of h5tojson, jsontoh5
*.log
# ignores output json of main.py
output/*.json
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/

0
output/placeholder Normal file
View File

View File

@@ -16,8 +16,9 @@ class Layer:
self.extra = layer_data["metadata_decoded"]["extra_fields"]
self.layer_number = self.extra["Layer Progressive Number"]["value"] # integer
self.target_elabid = self.extra["Target"]["value"] # elabid
self.rheed_system_elabid = self.extra["RHEED System"]["value"] # elabid
self.laser_system_elabid = self.extra["Laser System"]["value"] # elabid
self.chamber_elabid = self.extra["Chamber"]["value"] # elabid
self.rheed_system_elabid = self.extra["RHEED System"]["value"] # elabid
self.start_time = layer_data.get("created_at")
self.operator = layer_data.get("fullname")
self.description = layer_data.get("body")
@@ -71,6 +72,16 @@ class Layer:
# Some keys are not required and can be called through the .get() method - which is permissive and allows null values;
# Other keys are required so if they can't be called (invalid or null) raise error and stop execution of the program:
raise KeyError(f"The provided dictionary lacks a \"{k}\" key. Check the deposition layer entry on eLabFTW and make sure you used the correct Experiment template.")
def get_instruments(self, apikey):
raw_lasersys_data = APIHandler(apikey).get_entry_from_elabid(self.laser_system_elabid, entryType="items")
raw_chamber_data = APIHandler(apikey).get_entry_from_elabid(self.chamber_elabid, entryType="items")
raw_rheedsys_data = APIHandler(apikey).get_entry_from_elabid(self.rheed_system_elabid, entryType="items")
instruments_used = {
"laser_system": raw_lasersys_data.get("title") or None,
"deposition_chamber": raw_chamber_data.get("title") or None,
"rheed_system": raw_rheedsys_data.get("title") or None,
}
return instruments_used
class Entrypoint:
'''

View File

@@ -98,7 +98,37 @@ def chain_layer_to_target(layer_object):
material_object = call_material_from_elabid(target_elabid)
return material_object
def deduplicate_instruments_from_layers(layers):
'''
Takes a list of Layer-class objects and for each layer gets the instruments used (laser, depo chamber and RHEED), returns deduplicated list. Ideally, the lists should only contain one element.
'''
lasers = []
chambers = []
rheeds = []
for lyr in layers:
instruments = lyr.get_instruments(apikey)
lasers.append(instruments["laser_system"])
chambers.append(instruments["deposition_chamber"])
rheeds.append(instruments["rheed_system"])
instruments_used_dict = {
"laser_system": list( set( lasers ) ),
"deposition_chamber": list( set( chambers ) ),
"rheed_system" : list( set( rheeds ) ),
}
# lasers = { f"layer_{lyr.layer_number}": lyr.laser_system for lyr in layers }
# chambers = { f"layer_{lyr.layer_number}": lyr.deposition_chamber for lyr in layers }
# rheeds = { f"layer_{lyr.layer_number}": lyr.rheed_system for lyr in layers }
# instruments_used_dict = {
# "laser_system": lasers,
# "deposition_chamber": chambers,
# "rheed_system": rheeds,
# }
return instruments_used_dict
def make_nexus_schema_dictionary(substrate_object, layers):
'''
Main function, takes all the other functions to reconstruct the full dataset. Takes a Substrate-class object (output of the chain_entrypoint_to_batch() function) and a list of Layer-class objects (output of the chain_entrypoint_to_layers() function), returns dictionary with the same schema as the NeXus standard for PLD fabrications.
'''
pld_fabrication = {
"sample": {
"substrate": {
@@ -115,6 +145,7 @@ def make_nexus_schema_dictionary(substrate_object, layers):
},
"multilayer": {},
},
"instruments_used": deduplicate_instruments_from_layers(layers),
}
multilayer = pld_fabrication["sample"]["multilayer"]
for layer in layers:
@@ -133,9 +164,41 @@ def make_nexus_schema_dictionary(substrate_object, layers):
# "batch_id" : target_object.batch_id,
}
multilayer[name] = {
"target": target_dict
"target": target_dict,
"start_time": layer.start_time,
"operator": layer.operator,
"description": layer.description,
"number_of_pulses": layer.number_of_pulses,
"deposition_time": layer.deposition_time,
"temperature": layer.temperature,
"heating_method": layer.heating_method,
"layer_thickness": layer.layer_thickness,
"buffer_gas": layer.buffer_gas,
"process_pressure": layer.process_pressure,
"heater_target_distance": layer.heater_target_distance,
"repetition_rate": layer.repetition_rate,
"laser_fluence": layer.laser_fluence,
"laser_spot_area": layer.laser_spot_area,
"laser_energy": layer.laser_energy,
"laser_rastering": {
"geometry": layer.laser_rastering_geometry,
"positions": layer.laser_rastering_positions,
"velocities": layer.laser_rastering_velocities,
},
"pre_annealing": {
"ambient_gas": layer.pre_annealing_ambient_gas,
"pressure": layer.pre_annealing_pressure,
"temperature": layer.pre_annealing_temperature,
"duration": layer.pre_annealing_duration,
},
"post_annealing": {
"ambient_gas": layer.post_annealing_ambient_gas,
"pressure": layer.post_annealing_pressure,
"temperature": layer.post_annealing_temperature,
"duration": layer.post_annealing_duration,
},
}
return json.dumps(pld_fabrication, indent=2)
return pld_fabrication
if __name__=="__main__":
# TO-DO: place the API base URL somewhere else.
@@ -146,4 +209,7 @@ if __name__=="__main__":
sample = Entrypoint(data)
substrate_object = chain_entrypoint_to_batch(sample) # Substrate-class object
layers = chain_entrypoint_to_layers(sample) # list of Layer-class objects
print(make_nexus_schema_dictionary(substrate_object, layers)) # debug
result = make_nexus_schema_dictionary(substrate_object, layers)
# print(make_nexus_schema_dictionary(substrate_object, layers)) # debug
with open (f"output/sample-{elabid}.json", "w") as f:
json.dump(result, f, indent=3)