MionChrom NetCDF template

This describes a NetCDF file like suggested to use in MionChrom:

netcdf file:C:/Users/Fred/workspace/MionChrom/miofile_template.ncml {
 dimensions:
   number_of_internal_standards = 1;
   number_of_spiked_compounds = 1;
   number_of_system_components = 1;
   number_of_softwares = 1;
   number_of_injection_properties = 1;
   number_of_separation_properties = 1;
   number_of_detector_properties = 1;
   number_of_ms_properties = 1;
   number_of_other_system_properties = 1;
   number_of_sub_channels = 1;
   number_of_data_points = 1;
   two_dimensional_data = 2;
   number_of_sub_channels2 = 1;
   number_of_data_points2 = 1;
   number_of_sub_channels3 = 1;
   number_of_data_points3 = 1;
   number_of_sub_channels4 = 1;
   number_of_data_points4 = 1;
   number_of_sub_channels5 = 1;
   number_of_data_points5 = 1;
   number_of_sub_channels6 = 1;
   number_of_data_points6 = 1;
   number_of_peaks_detected = 1;
   number_of_calibrated_compounds = 1;
   number_of_calibration_properties = 7;
   calibration_history_length = 1;
   number_of_control_measurements = 3;
   number_of_control_compounds = 1;
   control_history_length = 1;
 variables:
   double internal_standard_values(number_of_internal_standards=1);
     :units = “grams per Liter”;
     :internal_standard_names = “None”;
     :comments = “None”;
     :long_name = “List of internal standards”;
   double spiked_compound_values(number_of_spiked_compounds=1);
     :units = “grams per Liter”;
     :spiked_compound_names = “None”;
     :comments = “None”;
     :long_name = “List of spiked compounds”;
   double injection_properties(number_of_injection_properties=1);
     :long_name = “Injection properties”;
     :property_names = “None”;
     :comments = “None”;
   double separation_properties(number_of_separation_properties=1);
     :long_name = “Separation properties”;
     :property_names = “None”;
     :comments = “None”;
   double detector_properties(number_of_detector_properties=1);
     :long_name = “Detector properties”;
     :property_names = “None”;
     :comments = “None”;
   double ms_properties(number_of_ms_properties=1);
     :long_name = “MS Detector Properties”;
     :property_names = “None”;
     :comments = “None”;
   double other_system_properties(number_of_other_system_properties=1);
     :long_name = “Other System Properties”;
     :property_names = “None”;
     :comments = “None”;
   double ordinate_values(number_of_sub_channels=1, two_dimensional_data=2, number_of_data_points=1);
     :long_name = “Signal as function of time”;
     :retention_units = “minutes”;
     :maximum_values = 1.0; // double
     :minimum_values = 0.0; // double
     :channel_name = “channel 1”;
     :units = “”;
     :actual_run_length = 0.0; // double
     :actual_sampling_interval = 0.0; // double
     :injectors = “None”;
     :syringes = “None”;
     :columns = “None”;
     :detectors = “None”;
     :comments = “None”;
     :detector_method = “None”;
     :injector_method = “None”;
     :separation_method = “None”;
     :channel_number = 1B; // byte
   double ordinate_values2(number_of_sub_channels2=1, two_dimensional_data=2, number_of_data_points2=1);
     :long_name = “Signal as function of time”;
     :retention_units = “minutes”;
     :maximum_values = 1.0; // double
     :minimum_values = 0.0; // double
     :channel_name = “channel 2”;
     :units = “”;
     :actual_run_length = 0.0; // double
     :actual_sampling_interval = 0.0; // double
     :injectors = “None”;
     :syringes = “None”;
     :columns = “None”;
     :detectors = “None”;
     :comments = “None”;
     :detector_method = “None”;
     :injector_method = “None”;
     :separation_method = “None”;
     :channel_number = 2B; // byte
   double ordinate_values3(number_of_sub_channels3=1, two_dimensional_data=2, number_of_data_points3=1);
     :long_name = “Signal as function of time”;
     :retention_units = “minutes”;
     :maximum_values = 1.0; // double
     :minimum_values = 0.0; // double
     :channel_name = “channel 3”;
     :units = “”;
     :actual_run_length = 0.0; // double
     :actual_sampling_interval = 0.0; // double
     :injectors = “None”;
     :syringes = “None”;
     :columns = “None”;
     :detectors = “None”;
     :comments = “None”;
     :detector_method = “None”;
     :injector_method = “None”;
     :separation_method = “None”;
     :channel_number = 3B; // byte
   double ordinate_values4(number_of_sub_channels4=1, two_dimensional_data=2, number_of_data_points4=1);
     :long_name = “Signal as function of time”;
     :retention_units = “minutes”;
     :maximum_values = 1.0; // double
     :minimum_values = 0.0; // double
     :channel_name = “channel 4”;
     :units = “”;
     :actual_run_length = 0.0; // double
     :actual_sampling_interval = 0.0; // double
     :injectors = “None”;
     :syringes = “None”;
     :columns = “None”;
     :detectors = “None”;
     :comments = “None”;
     :detector_method = “None”;
     :injector_method = “None”;
     :separation_method = “None”;
     :channel_number = 4B; // byte
   double ordinate_values5(number_of_sub_channels6=1, two_dimensional_data=2, number_of_data_points6=1);
     :long_name = “Signal as function of time”;
     :retention_units = “minutes”;
     :maximum_values = 1.0; // double
     :minimum_values = 0.0; // double
     :channel_name = “channel 6”;
     :units = “”;
     :actual_run_length = 0.0; // double
     :actual_sampling_interval = 0.0; // double
     :injectors = “None”;
     :syringes = “None”;
     :columns = “None”;
     :detectors = “None”;
     :comments = “None”;
     :detector_method = “None”;
     :injector_method = “None”;
     :separation_method = “None”;
     :channel_number = 6B; // byte
   double calibration_history(calibration_history_length=1, number_of_calibrated_compounds=1, number_of_calibration_properties=7);
     :long_name = “Calibration curve record”;
     :units = “”;
     :compound_names = “None”;
     :property_names = “number-of-data-points,slope,intercept,slope-error,intercept-error,random-error-in-y-direction,product-moment-correlation-factor”;
     :comments = “None”;
   double control_sample_history(control_history_length=1, number_of_control_compounds=1, number_of_control_measurements=3);
     :long_name = “Calibration curve record”;
     :units = “”;
     :compound_names = “None”;
     :comments = “”;
   byte manually_reintegrated_peaks(number_of_peaks_detected=1);
     :long_name = “Manual reintegrated peaks”;
     :units = “”;
     :comments = “None”;
     :integration_method = “Straight line approximation from start to end”;
   byte approximated_peaks(number_of_peaks_detected=1);
     :long_name = “Approximated peaks”;
     :units = “”;
     :comments = “Levenberg-Marquardt optimisation of sum of Simplified Exponential Modified Gaussians”;
     :approximation_methods = “sEMG”;
     :approximation_fits = 0.0; // double
     :approximation_references = “”;
   double peak_areas(number_of_peaks_detected=1);
     :long_name = “Area of peak”;
     :detection_method = “slope of five points above or under set limit”;
     :integration_method = “trapezoidal”;
     :units = “volts*minutes”;
     :comments = “None”;
   double peak_retention_times(number_of_peaks_detected=1);
     :long_name = “Retention time or migration time of peak”;
     :units = “minutes”;
     :apex_detection_method = “second derivative”;
     :comments = “None”;
   String peak_compound_names(number_of_peaks_detected=1);
     :long_name = “Name or identities of peak”;
     :units = “”;
     :identification_method = “Interval of set value”;
     :comments = “None”;
   double peak_results(number_of_peaks_detected=1);
     :long_name = “Results processed from area”;
     :units = “grams per Liter”;
     :calculation_method = “Response curve”;
     :comments = “None”;
   double peak_results_uncertainties(number_of_peaks_detected=1);
     :long_name = “Results processed from area”;
     :units = “grams per Liter”;
     :calculation_method = “Based from response curve”;
     :comments = “None”;
   byte peak_is_on_channel(number_of_peaks_detected=1);
     :long_name = “Reference to channel number”;
     :units = “”;
     :comments = “None”;
   byte peak_is_on_subchannel(number_of_peaks_detected=1);
     :long_name = “Reference to subchannel number within main channel”;
     :units = “”;
     :comments = “None”;
   double peak_starts(number_of_peaks_detected=1);
     :long_name = “Peak time at integration start”;
     :units = “minutes”;
     :comments = “None”;
     :method = “None”;
   double peak_ends(number_of_peaks_detected=1);
     :long_name = “Peak time at integration end”;
     :units = “minutes”;
     :comments = “None”;
     :method = “None”;
   double baseline_start_values(number_of_peaks_detected=1);
     :long_name = “Peak signal at integration start”;
     :units = “time”;
     :comments = “None”;
     :method = “None”;
   double baseline_end_values(number_of_peaks_detected=1);
     :long_name = “Peak signal at integration end”;
     :units = “minutes”;
     :comments = “None”;
     :method = “None”;
   double peak_heights(number_of_peaks_detected=1);
     :long_name = “Absolute peak height from apex or optimum to baseline”;
     :units = “volts”;
     :comments = “None”;
     :method = “None”;
   double peak_widths_50(number_of_peaks_detected=1);
     :long_name = “Peak width at 50 percent height”;
     :units = “minutes”;
     :comments = “None”;
     :method = “None”;
   double peak_asymmetries(number_of_peaks_detected=1);
     :long_name = “Peak assymetry”;
     :units = “”;
     :comments = “None”;
     :method = “None”;
   double peak_additional_data1(number_of_peaks_detected=1);
     :long_name = “Additional data 1”;
     :units = “”;
     :comments = “None”;
     :method = “None”;
   double peak_additional_data2(number_of_peaks_detected=1);
     :long_name = “Additional data 2”;
     :units = “”;
     :comments = “None”;
     :method = “None”;
   double peak_additional_data3(number_of_peaks_detected=1);
     :long_name = “Additional data 3”;
     :units = “”;
     :comments = “None”;
     :method = “None”;
   String peak_additional_data4(number_of_peaks_detected=1);
     :long_name = “Additional data 4”;
     :units = “”;
     :comments = “None”;
     :method = “None”;
   String peak_additional_data5(number_of_peaks_detected=1);
     :long_name = “Additional data 5”;
     :units = “”;
     :comments = “None”;
     :method = “None”;

 :Conventions = “CF-1.6”;
 :convention_references = “url:http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.6/cf-conventions.html”;
 :title = “Chromatography data”;
 :institution = “MionChrom software user”;
 :history = “Buildt from MionChrom template 1.0”;
 :source = “User input from chromatographic instruments.”;
 :comments = “MionChrom is a free and open-sourced software developed for intepreting chromatography data.”;
 :references = “url:mionchrom.sourceforge.net”;
 :date_created = “None”;
 :date_modified = “None”;
 :number_of_data_retention_days = 2000; // int
 :creator_name = “None”;
 :creator_email = “None”;
 :creator_url = “None”;
 :processing_level = “None”;
 :acknowledgment = “None”;
 :license = “None”;
 :funding = “None”;
 :naming_authority = “None”;
 :keywords = “None”;
 :summary = “None”;
 :dataset_owner = “None”;
 :id = “None”;
 :operator_name = “None”;
 :experiment = “None”;
 :template_version = 1.0f; // float
 :template_dateformat_standard = “YYYY-MM-dd HH:mm:ss”;
 :template_string_array_separator = “,”;
 :template_true = 1B; // byte
 :template_false = 0B; // byte
 :dataset_additional_data1 = “None”;
 :dataset_additional_data2 = “None”;
 :dataset_additional_data3 = “None”;
 :dataset_additional_data4 = “None”;
 :sample_id = “None”;
 :sample_name = “None”;
 :sample_comments = “None”;
 :project_name = “None”;
 :sample_matrix_type = “None”;
 :sample_container_type = “None”;
 :injection_data_type = “Sample”;
 :sample_autosampler_position = “None”;
 :sample_solvent = “None”;
 :source_file_reference = “None”;
 :source_file_format = “None”;
 :source_file_created = “None”;
 :customers_name = “None”;
 :customers_sample_id = “None”;
 :customers_sample_comments = “None”;
 :customers_references = “None”;
 :sampling_location = “None”;
 :sampling_time = “None”;
 :sampling_method = “None”;
 :sampling_comments = “None”;
 :pre_treatment_method = “None”;
 :pre_treatment_comments = “None”;
 :post_treatment_method = “None”;
 :post_treatment_comments = “None”;
 :needle_washing_solvents = “None”;
 :sample_rack_id = “None”;
 :contains_internal_standards = 0B; // byte
 :contains_spiked_compounds = 0B; // byte
 :system_comments = “None”;
 :instrument_components = “None”;
 :instrument_manufacturers = “None”;
 :instrument_models = “None”;
 :instrument_firmware_versions = “None”;
 :instrument_serial_numbers = “None”;
 :software_manufacturers = “MionChrom Development Team”;
 :software_versions = “MionChrom v2.0”;
 :operating_system = “None”;
 :instruments_additional_data1 = “None”;
 :instruments_additional_data2 = “None”;
 :instruments_additional_data3 = “None”;
 :instruments_additional_data4 = “None”;
 :contains_ms_detector = 0B; // byte
 :result_processing_comments = “None”;
 :result_processing_method = “None”;
 :result_processing_time = “None”;
 :result_processing_operator = “None”;
}

Introduction to the code, part 1

If you want to continue the MionChrom project I will give you a helpful introduction to the code in this post.

If you are not familiar with Java, you should learn it first. Learn yourself: datatypes, for, while, if, arrays, methods, inheritance, object-orientation, interfaces, ArrayList, HashMap, Swing-components, Layouthandlers. Java is very much like the C-class of programming languages, with some differences. One difference is the use of interfaces. Java is a cross-plattform language thanks to Java Virtual Machine (JVM) and should be working on Linux, Mac OS and Windows. You could use the softwares Eclipse or Netbeans to program Java. But if you want to go hardcore try Notepad or Notepad++ and compile the code with the “Javac” command in a console. I will recommend Eclipse in this project.

MionChrom is using Java.awt.Swing components for displaying the content (Genuine User Interface, GUI). Make yourself familiar with JComponent, JPanel, JTextArea, JLabel etc. A Layoutmanagers like BoxLayout is controlling how the components is arranged. ActionListeners, ChangeListeners, MouseListeners and other touch and feel interfaces is controlling interactions. Also make yourself familiar with the use of the paintComponent-method in combination with the Graphics-class. You can draw graphs, points, lines and text in different colors and styles on a JComponent by overriding this method. Image handling is also very useful to learn yourself. Especially how to draw images from file and BufferedImages.

For displaying the chromatograms MionChrom is using class called Dots which extends JPanel. It is override paintComponent and patched together two BufferedImages on top of each other and then draws selections like the rectangles that appears on the chromatogram when you click and drags the mouse. One BufferedImage holds the data and is redrawn when absolutely necessary like zooming and recentering as this is very resource demanding. The other information like text and lines and is redrawn on top of the other more frequently.

Each layer of chromatograms (like mass 44, 45 and 46) is represented as a “DataSer”-class (as in a dataseries). They holds the data in a double[][] array, where the first index is the axis, X is 0 and Y is 1. The second index is the datapoint number. To draw the data DataSer is converting the human number (I call them orga-numbers) to machine (mecha) numbers. Mecha-numbers are the coordinates on the JPanel as represented in Graphics. A frame-array is controlling what is shown as the user is centering and zooming. By storing the frame-array in a ArrayList you can enable the user to retrieve an earlier viewing point of the data. By storing the DataSer in the Dots-class in the same fashion the user can undo any changes in integration.

Welcome

This is the first post on the MionChrom blog. Here you will find information of updates and information of how to use the software. As our time is limited we can’t answer all the questions.

MionChrom is an open-sourced and free chromatographic integrator for calculations of stable isotopes for carbon on chromatograms from gas chromatography combustion isotope ratio mass spectrometry (GC-C-IRMS).

The software is programmed in Java, a cross-plattform language. The exetuable is a *.jar file, so for it to run on Mac or Linux you need to make a shell. Or you can start the program by command line by entering the folder of the program and write “java –jar MionChrom.jar”. On windows system you should be able to just double click the MionChrom jar-file. Select the method you want to use, and select a testfile to auto-integrate.

The project is published under General Public License 3 (GPL, http://en.wikipedia.org/wiki/GNU_General_Public_License ): So you are free to modify and redistribute the work as your own as long as the derivatives is published under GPL  and credits are given to the origins of the work. Also GPL assures that the source code is made available for all users. And protects the coder as it gives no warranty to the end-users.

Download the software here: http://sourceforge.net/projects/mionchrom/files/

Mionchrom 2.0 is the latest stable version.
MionChrom 2.1 got some pretty neat Approximation procedures of peaks

See a short demonstration on YouTube of MionChrom 2.1: http://www.youtube.com/watch?v=HJOVMy2nprA

Regarding support: Also log on Facebook: https://www.facebook.com/groups/mionchrom/

Since my time to spare to this project have gotten pretty slim recently, I have made a small list for others to continue the work. I will continue with this project when I get a sudden inspiration. But nothing stops you to just rip off the sourcecode, make your own software suits based on MionChrom. Just follow the GPL rules.

Suggestions to further work:

–          NetCDF as default save format. Now the project is using object serialization to save the batches of chromatograms.

–          Implement other stable isotope calculation procedures like hydrogen or sulphur. Currently the project only supports stable carbon isotopes.

–          Auto-save function. The auto-save on MionChrom 2.1 isn’t working properly.

–          Better identification system for peaks. Currently the project is using linear approximation of retention times through a model.

–          Implement other approximation methods of peaks. Currently only supports Simple Exponential Modified Gaussians, Bi-Gaussians, Gaussian-Lorentzians functions. Use the Apache Commons Maths very useful optimization suit to do this. If you could do some partial derivatives for me I can help implementing the functions for you (I used WolframAlpha.com, but it got a time limit for calculations now)

Please feel free to post questions and comments below.

Best regards

Fred