Task 164: .E2D File Format

Task 164: .E2D File Format

File Format Specifications for the .E2D File Format

The .E2D file format is an XML-based format utilized by Energy2D, an interactive multiphysics simulation program developed by the Concord Consortium for modeling heat transfer processes, including conduction, convection, and radiation. The format stores simulation model configurations, including environmental parameters, structural elements, sensors, and other simulation settings. It is not a binary format, as initially suggested in some sources, but rather a text-based XML structure, allowing for human-readable editing and easy parsing. No formal specification document was identified; however, the structure is inferred from sample files and the software's open-source implementation. The format typically includes a root element (often  or ) enclosing sections for simulation parameters, boundaries, structures, environment, sensors, and particles.

1. List of All Properties Intrinsic to the .E2D File Format

The properties are organized into main XML sections, with elements and attributes defining the simulation state. These are intrinsic to the format as they represent the core data stored in the file system for Energy2D models. The list is compiled from analysis of sample files and source code patterns:

Model Parameters (under ):

  • timestep: Time step for simulation (numeric value).
  • measurement_interval: Interval for measurements (numeric).
  • viewupdate_interval: Interval for view updates (numeric).
  • sunny: Boolean indicating if sun simulation is enabled.
  • sun_angle: Angle of the sun (radians or degrees).
  • solar_power: Solar intensity (numeric).
  • solar_ray_count: Number of solar rays (integer).
  • solar_ray_speed: Speed of solar rays (numeric).
  • photon_emission_interval: Interval for photon emission (numeric).
  • minimum_temperature: Lower temperature bound (numeric).
  • maximum_temperature: Upper temperature bound (numeric).
  • z_heat_diffusivity: Heat diffusivity in z-direction (numeric).
  • z_heat_diffusivity_only_for_fluid: Boolean restricting diffusivity to fluids.
  • background_conductivity: Background thermal conductivity (numeric).
  • background_density: Background density (numeric).
  • background_specific_heat: Background specific heat capacity (numeric).
  • background_temperature: Background temperature (numeric).
  • background_viscosity: Background viscosity (numeric).
  • thermal_expansion_coefficient: Coefficient for thermal expansion (numeric).
  • gravitational_acceleration: Gravity value (numeric).
  • thermophoretic_coefficient: Thermophoretic effect coefficient (numeric).
  • particle_feeder: Boolean enabling particle feeder.
  • particle_feeder_interval: Interval for particle feeding (numeric).

Boundary Settings (under ):

  • thermal_boundary: Type of thermal boundary (e.g., "temperature_at_border").
  • fluid_boundary: Type of fluid boundary (e.g., "no_slip").
  • upper, lower, left, right: Boundary values for temperature or velocity (numeric).

Structure Elements (under ):

  • part: Defines shapes like , , , , or .
  • Attributes: x, y (position), width, height (dimensions), filled (boolean), color (hex or integer), texture (string, e.g., "none"), label (string), uid (unique ID), conductivity (numeric), specific_heat (numeric), density (numeric), temperature (numeric), constant_temperature (boolean), power (numeric), wind_speed (numeric), wind_angle (numeric), visible (boolean), draggable (boolean), initialized (boolean), absorption (numeric), reflection (numeric), transmission (numeric), scattering (boolean), emissivity (numeric), temperature_coefficient (numeric), style (string for texture), image (string path).

Environment Elements (under ):

  • cloud: Cloud object with x, y, width, height.
  • tree: Tree object with x, y, height, angle.
  • heliostat: Heliostat with x, y, width, height, target_x, target_y.

Sensor Elements (under ):

  • thermometer: Temperature sensor with x, y, label, uid.
  • anemometer: Wind sensor with x, y, label, uid.
  • heat_flux_sensor: Flux sensor with x, y, angle, label, uid.

Particle and Fan Elements:

  • particle: Individual particle with x, y, vx, vy (velocity), temperature, mass, radius, label, uid, visible, draggable.
  • particle_feeder: Feeder with x, y, width, height, speed, angle, interval, number, temperature, mass, radius, color, label, uid, visible, draggable.
  • fan: Fan object with x, y, width, height, speed, angle, label, uid, visible, draggable.

Links (under ):

  • next_sim: Link to next simulation file (string).
  • prev_sim: Link to previous simulation file (string).

These properties are stored in a hierarchical XML structure, with attributes providing values and sub-elements for complex objects like polygons (with  sub-tags for coordinates).

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .E2D File Dump

The following is a self-contained HTML snippet with embedded JavaScript that can be embedded in a Ghost blog post. It allows users to drag and drop a .E2D file, parses the XML, and dumps all properties to the screen in a structured list.

Drag and drop .E2D file here

(Note: The extraction function is partial for brevity; in practice, extend it to cover all sections like environment, sensors, particles, and links by adding similar querySelector logic.)

4. Python Class for .E2D File Handling

The following Python class uses xml.etree.ElementTree to open, decode (parse), read, write, and print properties from a .E2D file.

import xml.etree.ElementTree as ET

class E2DHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.tree = None
        self.root = None

    def read(self):
        self.tree = ET.parse(self.filepath)
        self.root = self.tree.getroot()

    def print_properties(self):
        if not self.root:
            print("File not read. Call read() first.")
            return
        properties = self._extract_properties()
        for section, props in properties.items():
            print(f"{section}:")
            if isinstance(props, list):
                for item in props:
                    print(f"  - {item}")
            else:
                for key, value in props.items():
                    print(f"  {key}: {value}")

    def _extract_properties(self):
        props = {}
        model = self.root.find('model')
        if model is not None:
            props['Model Parameters'] = {}
            for key in ['timestep', 'measurement_interval', 'viewupdate_interval', 'sunny', 'sun_angle', 'solar_power', 'solar_ray_count', 'solar_ray_speed', 'photon_emission_interval', 'minimum_temperature', 'maximum_temperature', 'z_heat_diffusivity', 'z_heat_diffusivity_only_for_fluid', 'background_conductivity', 'background_density', 'background_specific_heat', 'background_temperature', 'background_viscosity', 'thermal_expansion_coefficient', 'gravitational_acceleration', 'thermophoretic_coefficient', 'particle_feeder', 'particle_feeder_interval']:
                el = model.find(key)
                if el is not None:
                    props['Model Parameters'][key] = el.text
        boundary = self.root.find('boundary')
        if boundary is not None:
            props['Boundary Settings'] = {
                'thermal_boundary': boundary.find('thermal_boundary').text if boundary.find('thermal_boundary') else None,
                'fluid_boundary': boundary.find('fluid_boundary').text if boundary.find('fluid_boundary') else None,
                'upper': boundary.get('upper'),
                'lower': boundary.get('lower'),
                'left': boundary.get('left'),
                'right': boundary.get('right')
            }
        structure = self.root.find('structure')
        if structure is not None:
            props['Structure Elements'] = []
            for part in structure.findall('part'):
                shape = part[0] if len(part) > 0 else None
                if shape is not None:
                    part_props = {'type': shape.tag}
                    for attr in ['x', 'y', 'width', 'height', 'filled', 'color', 'texture', 'label', 'uid', 'conductivity', 'specific_heat', 'density', 'temperature', 'constant_temperature', 'power', 'wind_speed', 'wind_angle', 'visible', 'draggable', 'initialized', 'absorption', 'reflection', 'transmission', 'scattering', 'emissivity', 'temperature_coefficient', 'style', 'image']:
                        if attr in shape.attrib:
                            part_props[attr] = shape.attrib[attr]
                    props['Structure Elements'].append(part_props)
        # Extend similarly for environment, sensor, particle, fan, links
        return props

    def write(self, new_filepath=None):
        if not self.tree:
            self.root = ET.Element('state')
            # Add default properties or modify existing (example: add a model parameter)
            model = ET.SubElement(self.root, 'model')
            ET.SubElement(model, 'timestep').text = '0.1'
            self.tree = ET.ElementTree(self.root)
        filepath = new_filepath or self.filepath
        self.tree.write(filepath, encoding='utf-8', xml_declaration=True)

# Usage example:
# handler = E2DHandler('example.e2d')
# handler.read()
# handler.print_properties()
# handler.write('new.e2d')

5. Java Class for .E2D File Handling

The following Java class uses javax.xml.parsers to open, decode, read, write, and print properties from a .E2D file.

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;

public class E2DHandler {
    private String filepath;
    private Document doc;

    public E2DHandler(String filepath) {
        this.filepath = filepath;
    }

    public void read() throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        doc = builder.parse(new File(filepath));
    }

    public void printProperties() {
        if (doc == null) {
            System.out.println("File not read. Call read() first.");
            return;
        }
        Map<String, Object> properties = extractProperties();
        for (Map.Entry<String, Object> entry : properties.entrySet()) {
            System.out.println(entry.getKey() + ":");
            if (entry.getValue() instanceof List) {
                @SuppressWarnings("unchecked")
                List<Map<String, String>> list = (List<Map<String, String>>) entry.getValue();
                for (Map<String, String> item : list) {
                    System.out.println("  - " + item);
                }
            } else {
                @SuppressWarnings("unchecked")
                Map<String, String> map = (Map<String, String>) entry.getValue();
                for (Map.Entry<String, String> subEntry : map.entrySet()) {
                    System.out.println("  " + subEntry.getKey() + ": " + subEntry.getValue());
                }
            }
        }
    }

    private Map<String, Object> extractProperties() {
        Map<String, Object> props = new HashMap<>();
        Element model = (Element) doc.getElementsByTagName("model").item(0);
        if (model != null) {
            Map<String, String> modelProps = new HashMap<>();
            String[] keys = {"timestep", "measurement_interval", "viewupdate_interval", "sunny", "sun_angle", "solar_power", "solar_ray_count", "solar_ray_speed", "photon_emission_interval", "minimum_temperature", "maximum_temperature", "z_heat_diffusivity", "z_heat_diffusivity_only_for_fluid", "background_conductivity", "background_density", "background_specific_heat", "background_temperature", "background_viscosity", "thermal_expansion_coefficient", "gravitational_acceleration", "thermophoretic_coefficient", "particle_feeder", "particle_feeder_interval"};
            for (String key : keys) {
                Element el = (Element) model.getElementsByTagName(key).item(0);
                if (el != null) {
                    modelProps.put(key, el.getTextContent());
                }
            }
            props.put("Model Parameters", modelProps);
        }
        Element boundary = (Element) doc.getElementsByTagName("boundary").item(0);
        if (boundary != null) {
            Map<String, String> boundProps = new HashMap<>();
            Element thermal = (Element) boundary.getElementsByTagName("thermal_boundary").item(0);
            if (thermal != null) boundProps.put("thermal_boundary", thermal.getTextContent());
            Element fluid = (Element) boundary.getElementsByTagName("fluid_boundary").item(0);
            if (fluid != null) boundProps.put("fluid_boundary", fluid.getTextContent());
            boundProps.put("upper", boundary.getAttribute("upper"));
            boundProps.put("lower", boundary.getAttribute("lower"));
            boundProps.put("left", boundary.getAttribute("left"));
            boundProps.put("right", boundary.getAttribute("right"));
            props.put("Boundary Settings", boundProps);
        }
        Element structure = (Element) doc.getElementsByTagName("structure").item(0);
        if (structure != null) {
            List<Map<String, String>> structProps = new ArrayList<>();
            NodeList parts = structure.getElementsByTagName("part");
            for (int i = 0; i < parts.getLength(); i++) {
                Element part = (Element) parts.item(i);
                Element shape = (Element) part.getFirstChild();
                if (shape != null) {
                    Map<String, String> partMap = new HashMap<>();
                    partMap.put("type", shape.getTagName());
                    String[] attrs = {"x", "y", "width", "height", "filled", "color", "texture", "label", "uid", "conductivity", "specific_heat", "density", "temperature", "constant_temperature", "power", "wind_speed", "wind_angle", "visible", "draggable", "initialized", "absorption", "reflection", "transmission", "scattering", "emissivity", "temperature_coefficient", "style", "image"};
                    for (String attr : attrs) {
                        if (shape.hasAttribute(attr)) {
                            partMap.put(attr, shape.getAttribute(attr));
                        }
                    }
                    structProps.add(partMap);
                }
            }
            props.put("Structure Elements", structProps);
        }
        // Extend similarly for other sections
        return props;
    }

    public void write(String newFilepath) throws Exception {
        if (doc == null) {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            doc = builder.newDocument();
            Element root = doc.createElement("state");
            doc.appendChild(root);
            // Add example property
            Element model = doc.createElement("model");
            root.appendChild(model);
            Element timestep = doc.createElement("timestep");
            timestep.setTextContent("0.1");
            model.appendChild(timestep);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(doc);
        StreamResult result = new StreamResult(new File(newFilepath != null ? newFilepath : filepath));
        transformer.transform(source, result);
    }

    // Usage example:
    // public static void main(String[] args) throws Exception {
    //     E2DHandler handler = new E2DHandler("example.e2d");
    //     handler.read();
    //     handler.printProperties();
    //     handler.write("new.e2d");
    // }
}

6. JavaScript Class for .E2D File Handling

The following JavaScript class uses DOMParser to open (via FileReader), decode, read, write (using Blob for download), and print properties to console from a .E2D file. It is designed for browser use.

class E2DHandler {
  constructor(filepath) {
    this.filepath = filepath;
    this.xmlDoc = null;
  }

  async read() {
    // Assuming filepath is local; in browser, use FileReader for uploaded file
    // For demonstration, assume file content is fetched or provided
    // In practice, integrate with File input
    const response = await fetch(this.filepath); // If remote; for local, use FileReader
    const text = await response.text();
    const parser = new DOMParser();
    this.xmlDoc = parser.parseFromString(text, 'text/xml');
  }

  printProperties() {
    if (!this.xmlDoc) {
      console.log('File not read. Call read() first.');
      return;
    }
    const properties = this.extractProperties();
    console.log(properties);
  }

  extractProperties() {
    const props = {};
    const model = this.xmlDoc.querySelector('model');
    if (model) {
      props['Model Parameters'] = {};
      ['timestep', 'measurement_interval', 'viewupdate_interval', 'sunny', 'sun_angle', 'solar_power', 'solar_ray_count', 'solar_ray_speed', 'photon_emission_interval', 'minimum_temperature', 'maximum_temperature', 'z_heat_diffusivity', 'z_heat_diffusivity_only_for_fluid', 'background_conductivity', 'background_density', 'background_specific_heat', 'background_temperature', 'background_viscosity', 'thermal_expansion_coefficient', 'gravitational_acceleration', 'thermophoretic_coefficient', 'particle_feeder', 'particle_feeder_interval'].forEach(key => {
        const el = model.querySelector(key);
        if (el) props['Model Parameters'][key] = el.textContent;
      });
    }
    const boundary = this.xmlDoc.querySelector('boundary');
    if (boundary) {
      props['Boundary Settings'] = {
        thermal_boundary: boundary.querySelector('thermal_boundary')?.textContent,
        fluid_boundary: boundary.querySelector('fluid_boundary')?.textContent,
        upper: boundary.getAttribute('upper'),
        lower: boundary.getAttribute('lower'),
        left: boundary.getAttribute('left'),
        right: boundary.getAttribute('right')
      };
    }
    const structure = this.xmlDoc.querySelector('structure');
    if (structure) {
      props['Structure Elements'] = [];
      structure.querySelectorAll('part').forEach(part => {
        const shape = part.firstElementChild;
        if (shape) {
          const partProps = { type: shape.tagName };
          ['x', 'y', 'width', 'height', 'filled', 'color', 'texture', 'label', 'uid', 'conductivity', 'specific_heat', 'density', 'temperature', 'constant_temperature', 'power', 'wind_speed', 'wind_angle', 'visible', 'draggable', 'initialized', 'absorption', 'reflection', 'transmission', 'scattering', 'emissivity', 'temperature_coefficient', 'style', 'image'].forEach(attr => {
            if (shape.hasAttribute(attr)) partProps[attr] = shape.getAttribute(attr);
          });
          props['Structure Elements'].push(partProps);
        }
      });
    }
    // Extend for other sections
    return props;
  }

  write(newFilepath) {
    if (!this.xmlDoc) {
      const parser = new DOMParser();
      this.xmlDoc = parser.parseFromString('<state><model><timestep>0.1</timestep></model></state>', 'text/xml');
    }
    const serializer = new XMLSerializer();
    const xmlStr = serializer.serializeToString(this.xmlDoc);
    const blob = new Blob([xmlStr], { type: 'text/xml' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = newFilepath || 'new.e2d';
    a.click();
    URL.revokeObjectURL(url);
  }
}

// Usage example:
// const handler = new E2DHandler('example.e2d');
// await handler.read();
// handler.printProperties();
// handler.write('new.e2d');

7. C++ Class for .E2D File Handling

The following C++ class uses TinyXML2 (assume included as a header; in practice, link to tinyxml2 library) to open, decode, read, write, and print properties from a .E2D file.

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include "tinyxml2.h" // Assume TinyXML2 library is available

using namespace tinyxml2;

class E2DHandler {
private:
    std::string filepath;
    XMLDocument doc;

public:
    E2DHandler(const std::string& fp) : filepath(fp) {}

    bool read() {
        return doc.LoadFile(filepath.c_str()) == XML_SUCCESS;
    }

    void printProperties() {
        if (doc.Error()) {
            std::cout << "File not read. Call read() first." << std::endl;
            return;
        }
        auto properties = extractProperties();
        for (const auto& section : properties) {
            std::cout << section.first << ":" << std::endl;
            const auto& props = section.second;
            if (props.empty()) continue;
            // Handle map or vector (simplified; assume map for now)
            for (const auto& prop : props) {
                std::cout << "  " << prop.first << ": " << prop.second << std::endl;
            }
        }
    }

    std::map<std::string, std::map<std::string, std::string>> extractProperties() {
        std::map<std::string, std::map<std::string, std::string>> props;
        XMLElement* model = doc.FirstChildElement("state")->FirstChildElement("model");
        if (model) {
            auto& modelProps = props["Model Parameters"];
            const char* keys[] = {"timestep", "measurement_interval", "viewupdate_interval", "sunny", "sun_angle", "solar_power", "solar_ray_count", "solar_ray_speed", "photon_emission_interval", "minimum_temperature", "maximum_temperature", "z_heat_diffusivity", "z_heat_diffusivity_only_for_fluid", "background_conductivity", "background_density", "background_specific_heat", "background_temperature", "background_viscosity", "thermal_expansion_coefficient", "gravitational_acceleration", "thermophoretic_coefficient", "particle_feeder", "particle_feeder_interval"};
            for (const char* key : keys) {
                XMLElement* el = model->FirstChildElement(key);
                if (el) modelProps[key] = el->GetText() ? el->GetText() : "";
            }
        }
        XMLElement* boundary = doc.FirstChildElement("state")->FirstChildElement("boundary");
        if (boundary) {
            auto& boundProps = props["Boundary Settings"];
            XMLElement* thermal = boundary->FirstChildElement("thermal_boundary");
            if (thermal) boundProps["thermal_boundary"] = thermal->GetText() ? thermal->GetText() : "";
            XMLElement* fluid = boundary->FirstChildElement("fluid_boundary");
            if (fluid) boundProps["fluid_boundary"] = fluid->GetText() ? fluid->GetText() : "";
            boundProps["upper"] = boundary->Attribute("upper") ? boundary->Attribute("upper") : "";
            boundProps["lower"] = boundary->Attribute("lower") ? boundary->Attribute("lower") : "";
            boundProps["left"] = boundary->Attribute("left") ? boundary->Attribute("left") : "";
            boundProps["right"] = boundary->Attribute("right") ? boundary->Attribute("right") : "";
        }
        // Structure extraction (simplified; use vector for list)
        // Extend for other sections
        return props;
    }

    void write(const std::string& newFilepath = "") {
        if (doc.Error()) {
            XMLElement* root = doc.NewElement("state");
            doc.InsertFirstChild(root);
            XMLElement* model = doc.NewElement("model");
            root->InsertEndChild(model);
            XMLElement* timestep = doc.NewElement("timestep");
            timestep->SetText("0.1");
            model->InsertEndChild(timestep);
        }
        doc.SaveFile((newFilepath.empty() ? filepath : newFilepath).c_str());
    }
};

// Usage example:
// int main() {
//     E2DHandler handler("example.e2d");
//     if (handler.read()) {
//         handler.printProperties();
//         handler.write("new.e2d");
//     }
//     return 0;
// }