User Tools

Site Tools


dosimetry:userguide:thinknode

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
dosimetry:userguide:thinknode [2015/09/24 18:32] – [SOBP Dose Calculation] dpatenaudedosimetry:userguide:thinknode [2021/07/29 18:23] (current) – external edit 127.0.0.1
Line 6: Line 6:
 ====== Python ====== ====== Python ======
  
 +Please refer to the [[https://github.com/dotdecimal/astroid-script-library/blob/development/python/README.md| Python Script Library Readme]] for dependent python modules and a high level list of what these libraries include.
 ===== Python: Overview ===== ===== Python: Overview =====
  
Line 152: Line 153:
 # Get IAM ids # Get IAM ids
 iam = thinknode.authenticate(thinknode.read_config('thinknode.cfg')) iam = thinknode.authenticate(thinknode.read_config('thinknode.cfg'))
- 
-def make_grid(corner, size, spacing): 
-... 
- 
-def make_water_phantom(corner, size, spacing): 
-    return \ 
-        thinknode.function("dosimetry", "create_uniform_image_on_grid_3d", 
-            [ 
-                make_grid(corner, size, spacing), 
-                thinknode.value(1), 
-                thinknode.value("relative_stopping_power") 
-            ]) 
  
 def make_dose_points(pointCount): def make_dose_points(pointCount):
Line 213: Line 202:
     thinknode.function("dosimetry", "compute_sobp_pb_dose2",     thinknode.function("dosimetry", "compute_sobp_pb_dose2",
         [         [
-            make_water_phantom([-100, -100, -100], [200, 200, 200], [2, 2, 2]), #stopping_power_image+            dosimetry.make_image_3d(iam, [-100, -100, -100], [200, 200, 200], [2, 2, 2], 1), #stopping_power_image
             thinknode.value(make_dose_points(181)), # dose_points             thinknode.value(make_dose_points(181)), # dose_points
             beam_geometry, #beam_geometry             beam_geometry, #beam_geometry
-            make_grid([-75, -75], [150, 150], [2, 2]), # bixel_grid+            dosimetry.make_grid(iam, [-75, -75], [150, 150], [2, 2]), # bixel_grid
             make_layers(2270.0, 152.0, 38.0),             make_layers(2270.0, 152.0, 38.0),
             compute_aperture(), # aperture based on targets             compute_aperture(), # aperture based on targets
Line 230: Line 219:
 ==== rt_types ==== ==== rt_types ====
  
-The //rt_types// module is a reconstruction of astroid manifest types in python class format. This includes interdependencies between types (e.g. the class "aperture_creation_params.view" requires the class "multiple_source_view").+The //rt_types// module is a reconstruction of all astroid types in python class format. This includes interdependencies between types (e.g. the class "polyset" requires the class "polygon2").
  
 Each data type detailed in the [[http://docs.apps.dotdecimal.com|astroid Manifest Guide]] has a corresponding class in this python module.  Each data type detailed in the [[http://docs.apps.dotdecimal.com|astroid Manifest Guide]] has a corresponding class in this python module. 
  
- +Below you will see snippet from the rt_types module that shows the class for the //polyset// rt_type along with its default initialization, //expand_data// and //from_json// functions.
-Below you will see as snippet from the rt_types module that shows the class for the //aperture_creation_params// rt_type along with its default initializations and //.out// function. +
-  * **Interdependence:** When rt_types are constructed of other or multiple named typesthey will be constructed as such in each class as displayed by the //view// parameter of the //aperture_creation_params// in this example. The [[dosimetry:userguide:thinknode#sobp_dose_calculation|sobp dose calculation]] sample python script provides an example of this usage in actual practice. +
-  * **//out// function:** Each class's //.out// function provides an ordered dictionary of each of the values in the class. This is explicitly an ordered dictionary since when calling a function in a calculation request, the order of the values provided matters if constructing the request by thinknode value type.+
  
 <code python> <code python>
-class aperture_creation_params(object):+class polygon2(object): 
 + 
 + #Initialize 
 + def __init__(self): 
 + blob = blob_type() 
 + self.vertices = blob.toStr() 
 + 
 + def expand_data(self): 
 + data = {} 
 + data['vertices'] = parse_bytes_2d(base64.b64decode(self.vertices['blob'])) 
 + return data 
 + 
 + def from_json(self, jdict): 
 + for k, v in jdict.items(): 
 + if hasattr(self,k): 
 + setattr(self, k, v) 
 + 
 +class polyset(object):
  
  #Initialize  #Initialize
  def __init__(self):  def __init__(self):
- self.targets = []  + self.polygons = []  
- self.target_margin = 0.0  + self.holes = [] 
- self.view = multiple_source_view() +
- self.mill_radius = 0.0  +
- self.organs = []  +
- self.half_planes = []  +
- self.corner_planes = []  +
- self.centerlines = []  +
- self.overrides = []  +
- self.downstream_edge = 0.0 +
  
  def expand_data(self):  def expand_data(self):
  data = {}  data = {}
- target = [] + polygon = [] 
- for x in self.targets+ for x in self.polygons
- s = triangle_mesh()+ s = polygon2()
  s.from_json(x)  s.from_json(x)
- target.append(s.expand_data()) + polygon.append(s.expand_data()) 
- data['targets'] = target + data['polygons'] = polygon 
- data['target_margin'] = self.target_margin + hole = [] 
- data['view'] = self.view.expand_data() + for x in self.holes
- data['mill_radius'] = self.mill_radius + s = polygon2()
- organ = [] +
- for x in self.organs+
- s = aperture_organ()+
  s.from_json(x)  s.from_json(x)
- organ.append(s.expand_data()) + hole.append(s.expand_data()) 
- data['organs'] = organ + data['holes'] = hole
- half_plane = [] +
- for x in self.half_planes: +
- s = aperture_half_plane() +
- s.from_json(x) +
- half_plane.append(s.expand_data()) +
- data['half_planes'] = half_plane +
- corner_plane = [] +
- for x in self.corner_planes: +
- s = aperture_corner_plane() +
- s.from_json(x) +
- corner_plane.append(s.expand_data()) +
- data['corner_planes'] = corner_plane +
- centerline = [] +
- for x in self.centerlines: +
- s = aperture_centerline() +
- s.from_json(x) +
- centerline.append(s.expand_data()) +
- data['centerlines'] = centerline +
- override = [] +
- for x in self.overrides: +
- s = aperture_manual_override() +
- s.from_json(x) +
- override.append(s.expand_data()) +
- data['overrides'] = override +
- data['downstream_edge'] = self.downstream_edge+
  return data  return data
  
Line 302: Line 269:
  for k, v in jdict.items():  for k, v in jdict.items():
  if hasattr(self,k):  if hasattr(self,k):
- if == 'view'+ setattr(self, k, v) 
- self.view.from_json(v+</code> 
- else+ 
- setattr(selfkv)+  * **Interdependence:** When rt_types are constructed of other or multiple named types, they will be constructed as such in each class as seen in the //polygons// parameter of the //polyset// in the above example. 
 +  * **//expand_data// function:** Each class's //expand_data// function returns a python dictionary containing each of the values in the class, with all data values expanded out to remove compression or other encodings (i.eproviding results in a format more useful for send to other applications or for human-readability). 
 +  * **//from_json// function:** Each class's //from_json// function provides a method to turn a raw json string (e.g. a result from a thinknode calculation or ISS objectinto an rt_type data type. Proper use is to first construct an empty class instance, then to call the //from_json// method on that instance, passing in the desired json data string.  
 + 
 +Below is an example usage of getting a thinknode dose image (image_3d data type in the astroid manifest) and turning it into a rt_types image_3d data type, so that it can be expanded and then used to output the image into a VTK graphics file
 + 
 +<code python> 
 +def dose_to_vtk(dose_id): 
 +    img_data = json.loads(thinknode.get_immutable(iam'dicom'dose_id)) 
 + 
 +    img = rt_types.image_3d() 
 +    img.from_json(img_data) 
 +    img2 = img.expand_data() 
 + 
 +    vtk.write_vtk_image3('E:/dicom/dose.vtk', img2)
 </code> </code>
 ==== thinknode_worker ==== ==== thinknode_worker ====
Line 311: Line 292:
 The //thinknode_worker// module is the main work horse for communication with the astroid app and thinknode. The module will handle authentication, posting objects to ISS, creating most of the common calculation request structures, and posting the calculation request. The //thinknode_worker// module is the main work horse for communication with the astroid app and thinknode. The module will handle authentication, posting objects to ISS, creating most of the common calculation request structures, and posting the calculation request.
  
-Refer to the [[https://github.com/dotdecimal/astroid-script-library|.decimal GitHub repository]] for the complete module. Below are a few of the more common thinknode http worker and their intended usages:+Refer to the [[https://github.com/dotdecimal/astroid-script-library|.decimal GitHub repository]] for the complete module. Below are a few of the more common thinknode_worker functions and their intended usages:
  
 <code python> <code python>
-# Authenticate with thinknode and store necessary ids +# Authenticate with thinknode and store necessary ids. 
-# Gets the realm_id, bucket_id, and context_id for the current iam configuration+# Gets the context id for each app detailed in the thinknode config 
 +# Gets the app version (if non defined) for each app in the realm
 #   param config: connection settings (url and unique basic user authentication) #   param config: connection settings (url and unique basic user authentication)
 def authenticate(config): def authenticate(config):
  
-# Send calculation request to thinknode api+# Send calculation request to thinknode and wait for the calculation to perform. Caches locally calculation results so if the same  
 +# calculation is performed again, the calculation 
 +# does not have to be repeatedly pulled from thinknode. Saves one calculation time and bandwidth. 
 +#   note: see post_calculation if you just want the calculation ID and don't need to wait for the calculation to finish or get results
 #   param config: connection settings (url, user token, and ids for context and realm) #   param config: connection settings (url, user token, and ids for context and realm)
 #   param json_data: calculation request in json format #   param json_data: calculation request in json format
-#   param return_data: True = returns calculation result; False = returns calculation id +#   param return_data: When True the data object will be returned, when false the thinknode id for the object will be returned 
-def do_calculation(config, json_data, return_data=True):+#   param return_error: When False the script will exit when error is found, when True the sciprt will return the error 
 +def do_calculation(config, json_data, return_data=True, return_error=False):
  
-# Post immutable object to ISS+# Post immutable named_type object to ISS
 #   param config: connection settings (url, user token, and ids for context and realm) #   param config: connection settings (url, user token, and ids for context and realm)
 +#   param app_name: name of the app to use to get the context id from the iam config
 #   param json_data: immutable object in json format #   param json_data: immutable object in json format
 #   param obj_name: object name of app to post to #   param obj_name: object name of app to post to
-def post_immutable(config, json_data, obj_name):+def post_immutable_named(config, app_name, json_data, obj_name): 
 +    scope = '/iss/named/' + config["account_name"] + '/rt_types' + '/' + obj_name 
 +    return post_immutable(config, app_name, json_data, scope)
  
 # Post immutable object to ISS # Post immutable object to ISS
 #   param config: connection settings (url, user token, and ids for context and realm) #   param config: connection settings (url, user token, and ids for context and realm)
 +#   param app_name: name of the app to use to get the context id from the iam config
 #   param obj_id: thinknode iss reference id for object to get #   param obj_id: thinknode iss reference id for object to get
-def get_immutable(config, obj_id):+def get_immutable(config, app_name, obj_id):
  
  
 +</code>
 +
 +==== dosimetry_worker ====
 +The dosimetry_worker module provides high-level functions for building data types and calculation requests for common dosimetry tasks. This library is constantly growing as more routine tasks are programmed in python. 
 +
 +Refer to the [[https://github.com/dotdecimal/astroid-script-library|.decimal GitHub repository]] for the complete module. Some basic examples of provided functionality are:
 +
 +  - Aperture creation (using structures/beams or basic geometric)
 +  - Dose comparison
 +  - Grid creation
 +  - Image creation
 +  - PBS Spot functions
 +
 +
 +==== vtk_worker ====
 +The VTK worker provides a means to write out common rt_types to a vtk file format ([[http://www.vtk.org/|The Visualization TooKit]]) that can be visualized in [[http://www.paraview.org/|Paraview]]. It's most useful for displaying and post-processing image, mesh, and other primitive object data types.
 +
 +Below is an example of turning a dose image_3d into a vtk file for visualization in Paraview:
 +
 +<code python>
 +def dose_to_vtk(dose_id):
 +    img_data = json.loads(thinknode.get_immutable(iam, 'dicom', dose_id))
 +
 +    img = rt_types.image_3d()
 +    img.from_json(img_data)
 +    img2 = img.expand_data()
 +
 +    vtk.write_vtk_image3('E:/dicom/dose.vtk', img2)
 </code> </code>
  
 ==== decimal_logging ==== ==== decimal_logging ====
  
-The //decimal_logging// module provides formatted and detailed output window and file logging. +The //decimal_logging// module provides formatted and detailed output window messages and file logging. 
  
 The following settings are available in the decimal_logging.py file: The following settings are available in the decimal_logging.py file:
Line 347: Line 365:
 **display_types: ** display message types (e.g. debug, data, alert) in the output window/logfile **display_types: ** display message types (e.g. debug, data, alert) in the output window/logfile
 **log_file: ** sets the logfile name and location **log_file: ** sets the logfile name and location
 +
 +=== Debugging ===
 +
 +When debugging, use the dl.debug() function and set the //isDebug// flag in the decimal_logging library to True. This toggles on the output for each of the dl.debug calls. By default we keep debugging off, but it can be turned on as needed.
 +
 +=== Other Flags ===
 +
  
 The following image shows the logging settings for each message type as: The following image shows the logging settings for each message type as:
Line 354: Line 379:
  
 {{ dosimetry:userguide:decimal_logging_example.png |}} {{ dosimetry:userguide:decimal_logging_example.png |}}
 +
 +=== File Logging ===
 +
 +The decimal_logging library also provides simple file logging. The //log_file// variable at the top of the library sets the log file. By using any of the following functions, you can easily log data to the specified file:
 +
 +  * log(message)
 +  * log_debug_data(message,data)
 +  * log_data(data)
 +
 ---- ----
 <WRAP center 10%>//USR-001//</WRAP> <WRAP center 10%>//USR-001//</WRAP>
  
 <WRAP center 40%>.decimal LLC, 121 Central Park Place, Sanford, FL. 32771</WRAP> <WRAP center 40%>.decimal LLC, 121 Central Park Place, Sanford, FL. 32771</WRAP>
dosimetry/userguide/thinknode.1443119543.txt.gz · Last modified: 2021/07/29 18:21 (external edit)