diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 0000000000000000000000000000000000000000..ceddbda592c2c92fc2c99e5b2650ade0f6142564
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,2 @@
+[env]
+OPM_FILE_VERSION = "0"
diff --git a/Cargo.toml b/Cargo.toml
index d3a6a9ee8accd9dc297336ba8dd7bbb03aa5111d..78b45eaba2958eec475354ad7127688f7591f6a0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,7 +9,6 @@ license = "GPL-3.0-or-later"
 keywords = ["laser", "optics", "physics", "simulation"]
 categories = [ "science", "simulation"]
 publish = false
-
 build = "build.rs"
 
 [dependencies]
diff --git a/src/optic_scenery.rs b/src/optic_scenery.rs
index e5afbaa23932706c2c9d54e39fabb6da39169c9f..e82fcb70d478c1881944cb4cd2886ab601266e52 100644
--- a/src/optic_scenery.rs
+++ b/src/optic_scenery.rs
@@ -16,7 +16,9 @@ use chrono::Local;
 use petgraph::algo::*;
 use petgraph::prelude::NodeIndex;
 use petgraph::visit::EdgeRef;
-use serde_derive::{Deserialize, Serialize};
+use serde::de::{self, MapAccess, Visitor};
+use serde::ser::SerializeStruct;
+use serde::{Deserialize, Serialize};
 
 /// Overall optical model and additional metatdata.
 ///
@@ -39,11 +41,9 @@ use serde_derive::{Deserialize, Serialize};
 /// }
 ///
 /// ```
-#[derive(Debug, Serialize, Deserialize, Clone)]
+#[derive(Debug, Clone)]
 pub struct OpticScenery {
-    #[serde(rename = "graph")]
     g: OpticGraph,
-    #[serde(rename = "properties")]
     props: Properties,
 }
 
@@ -246,7 +246,6 @@ impl OpticScenery {
             .0
             .node_weights()
             .filter(|node| node.0.borrow().is_detector());
-        //report.insert("detectors".into(), json!("Detectors"));
         let mut detectors: Vec<serde_json::Value> = Vec::new();
         for node in detector_nodes {
             detectors.push(node.0.borrow().report());
@@ -277,7 +276,118 @@ impl OpticScenery {
         Ok(())
     }
 }
+impl Serialize for OpticScenery {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        let mut scene = serializer.serialize_struct("scenery", 3)?;
+        scene.serialize_field("opm version", &env!("OPM_FILE_VERSION"))?;
+        scene.serialize_field("graph", &self.g)?;
+        scene.serialize_field("properties", &self.props)?;
+        scene.end()
+    }
+}
+impl<'de> Deserialize<'de> for OpticScenery {
+    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        enum Field {
+            OpmVersion,
+            Graph,
+            Properties,
+        }
+        const FIELDS: &[&str] = &["opm version", "graph", "properties"];
+
+        impl<'de> Deserialize<'de> for Field {
+            fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+            where
+                D: serde::Deserializer<'de>,
+            {
+                struct FieldVisitor;
+
+                impl<'de> Visitor<'de> for FieldVisitor {
+                    type Value = Field;
+
+                    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+                        formatter.write_str("`opm version`, `graph`, or `properties`")
+                    }
+                    fn visit_str<E>(self, value: &str) -> std::result::Result<Field, E>
+                    where
+                        E: de::Error,
+                    {
+                        match value {
+                            "opm version" => Ok(Field::OpmVersion),
+                            "graph" => Ok(Field::Graph),
+                            "properties" => Ok(Field::Properties),
+                            _ => Err(de::Error::unknown_field(value, FIELDS)),
+                        }
+                    }
+                }
+                deserializer.deserialize_identifier(FieldVisitor)
+            }
+        }
 
+        struct OpticSceneryVisitor;
+
+        impl<'de> Visitor<'de> for OpticSceneryVisitor {
+            type Value = OpticScenery;
+
+            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+                formatter.write_str("an OpticScenery")
+            }
+            fn visit_map<A>(self, mut map: A) -> std::result::Result<OpticScenery, A::Error>
+            where
+                A: MapAccess<'de>,
+            {
+                let mut opm_version: Option<String> = None;
+                let mut graph: Option<OpticGraph> = None;
+                let mut properties: Option<Properties> = None;
+                while let Some(key) = map.next_key()? {
+                    match key {
+                        Field::OpmVersion => {
+                            if opm_version.is_some() {
+                                return Err(de::Error::duplicate_field("opm version"));
+                            }
+                            opm_version = Some(map.next_value()?);
+                        }
+                        Field::Graph => {
+                            if graph.is_some() {
+                                return Err(de::Error::duplicate_field("graph"));
+                            }
+                            graph = Some(map.next_value()?);
+                        }
+                        Field::Properties => {
+                            if properties.is_some() {
+                                return Err(de::Error::duplicate_field("properties"));
+                            }
+                            properties = Some(map.next_value()?);
+                        }
+                    }
+                }
+                if let Some(opm_version) = opm_version {
+                    if opm_version != env!("OPM_FILE_VERSION") {
+                        println!(
+                            "\nWarning: version mismatch! File version {}, Appplication version {}",
+                            opm_version,
+                            env!("OPM_FILE_VERSION")
+                        );
+                    }
+                }
+                let graph = graph.ok_or_else(|| de::Error::missing_field("graph"))?;
+                let properties =
+                    properties.ok_or_else(|| de::Error::missing_field("properties"))?;
+
+                Ok(OpticScenery {
+                    g: graph,
+                    props: properties,
+                })
+            }
+        }
+        deserializer.deserialize_struct("OpticScenery", FIELDS, OpticSceneryVisitor)
+    }
+}
 #[cfg(test)]
 mod test {
     use crate::nodes::Metertype;