1use serde::Serialize;
9
10#[derive(Serialize)]
12pub struct Face3D {
13 #[serde(rename = "type")]
14 pub ty: &'static str, pub boundary: Vec<[f64; 3]>,
16}
17
18impl Face3D {
19 pub fn new(boundary: Vec<[f64; 3]>) -> Self {
20 Self { ty: "Face3D", boundary }
21 }
22}
23
24#[derive(Serialize)]
27pub struct BoundaryCondition {
28 #[serde(rename = "type")]
29 pub ty: &'static str, #[serde(skip_serializing_if = "Option::is_none")]
31 pub boundary_condition_objects: Option<Vec<String>>,
32}
33
34impl BoundaryCondition {
35 pub fn new(ty: &'static str) -> Self {
36 Self { ty, boundary_condition_objects: None }
37 }
38 pub fn surface(adjacent_face: String, adjacent_room: String) -> Self {
40 Self { ty: "Surface", boundary_condition_objects: Some(vec![adjacent_face, adjacent_room]) }
41 }
42}
43
44#[derive(Serialize)]
45pub struct TypedProps {
46 #[serde(rename = "type")]
47 pub ty: &'static str,
48}
49
50#[derive(Serialize, Clone)]
54pub struct EnergyMaterial {
55 #[serde(rename = "type")]
56 pub ty: &'static str, pub identifier: String,
58 pub roughness: &'static str, pub thickness: f64,
60 pub conductivity: f64,
61 pub density: f64,
62 pub specific_heat: f64,
63 pub thermal_absorptance: f64,
64 pub solar_absorptance: f64,
65 pub visible_absorptance: f64,
66}
67
68impl EnergyMaterial {
69 pub fn new(identifier: String, thickness: f64, conductivity: f64, density: f64, specific_heat: f64) -> Self {
70 Self {
71 ty: "EnergyMaterial",
72 identifier,
73 roughness: "MediumRough",
74 thickness,
75 conductivity,
76 density,
77 specific_heat,
78 thermal_absorptance: 0.9,
79 solar_absorptance: 0.7,
80 visible_absorptance: 0.7,
81 }
82 }
83}
84
85#[derive(Serialize, Clone)]
87pub struct OpaqueConstruction {
88 #[serde(rename = "type")]
89 pub ty: &'static str, pub identifier: String,
91 pub materials: Vec<String>,
92}
93
94impl OpaqueConstruction {
95 pub fn new(identifier: String, materials: Vec<String>) -> Self {
96 Self { ty: "OpaqueConstructionAbridged", identifier, materials }
97 }
98}
99
100#[derive(Serialize)]
102pub struct FaceEnergy {
103 #[serde(rename = "type")]
104 pub ty: &'static str, #[serde(skip_serializing_if = "Option::is_none")]
106 pub construction: Option<String>,
107}
108
109#[derive(Serialize)]
110pub struct FaceProperties {
111 #[serde(rename = "type")]
112 pub ty: &'static str, #[serde(skip_serializing_if = "Option::is_none")]
114 pub energy: Option<FaceEnergy>,
115}
116
117#[derive(Serialize)]
119pub struct ModelEnergy {
120 #[serde(rename = "type")]
121 pub ty: &'static str, pub materials: Vec<EnergyMaterial>,
123 pub constructions: Vec<OpaqueConstruction>,
124}
125
126#[derive(Serialize)]
127pub struct ModelProperties {
128 #[serde(rename = "type")]
129 pub ty: &'static str, #[serde(skip_serializing_if = "Option::is_none")]
131 pub energy: Option<ModelEnergy>,
132}
133
134#[derive(Serialize)]
136pub struct Aperture {
137 #[serde(rename = "type")]
138 pub ty: &'static str, pub identifier: String,
140 pub display_name: String,
141 pub properties: TypedProps,
142 pub geometry: Face3D,
143 pub is_operable: bool,
144 pub boundary_condition: BoundaryCondition,
145}
146
147impl Aperture {
148 pub fn new(identifier: String, geometry: Face3D, is_operable: bool) -> Self {
149 let display_name = identifier.clone();
150 Self {
151 ty: "Aperture",
152 identifier,
153 display_name,
154 properties: TypedProps { ty: "AperturePropertiesAbridged" },
155 geometry,
156 is_operable,
157 boundary_condition: BoundaryCondition::new("Outdoors"),
158 }
159 }
160}
161
162#[derive(Serialize)]
164pub struct Door {
165 #[serde(rename = "type")]
166 pub ty: &'static str, pub identifier: String,
168 pub display_name: String,
169 pub properties: TypedProps,
170 pub geometry: Face3D,
171 pub is_glass: bool,
172 pub boundary_condition: BoundaryCondition,
173}
174
175impl Door {
176 pub fn new(identifier: String, geometry: Face3D, is_glass: bool) -> Self {
177 let display_name = identifier.clone();
178 Self {
179 ty: "Door",
180 identifier,
181 display_name,
182 properties: TypedProps { ty: "DoorPropertiesAbridged" },
183 geometry,
184 is_glass,
185 boundary_condition: BoundaryCondition::new("Outdoors"),
186 }
187 }
188}
189
190#[derive(Serialize)]
193pub struct Mesh3D {
194 #[serde(rename = "type")]
195 pub ty: &'static str, pub vertices: Vec<[f64; 3]>,
197 pub faces: Vec<[usize; 3]>,
198}
199
200#[derive(Serialize)]
201pub struct ShadeMesh {
202 #[serde(rename = "type")]
203 pub ty: &'static str, pub identifier: String,
205 pub display_name: String,
206 pub properties: TypedProps,
207 pub geometry: Mesh3D,
208}
209
210impl ShadeMesh {
211 pub fn new(identifier: String, vertices: Vec<[f64; 3]>, faces: Vec<[usize; 3]>) -> Self {
212 let display_name = identifier.clone();
213 Self {
214 ty: "ShadeMesh",
215 identifier,
216 display_name,
217 properties: TypedProps { ty: "ShadeMeshPropertiesAbridged" },
218 geometry: Mesh3D { ty: "Mesh3D", vertices, faces },
219 }
220 }
221}
222
223#[derive(Serialize)]
225pub struct Face {
226 #[serde(rename = "type")]
227 pub ty: &'static str, pub identifier: String,
229 pub display_name: String,
230 pub properties: FaceProperties,
231 pub geometry: Face3D,
232 pub face_type: &'static str,
233 pub boundary_condition: BoundaryCondition,
234 #[serde(skip_serializing_if = "Vec::is_empty")]
235 pub apertures: Vec<Aperture>,
236 #[serde(skip_serializing_if = "Vec::is_empty")]
237 pub doors: Vec<Door>,
238}
239
240impl Face {
241 pub fn new(identifier: String, geometry: Face3D, face_type: &'static str, bc: &'static str) -> Self {
242 let display_name = identifier.clone();
243 Self {
244 ty: "Face",
245 identifier,
246 display_name,
247 properties: FaceProperties { ty: "FacePropertiesAbridged", energy: None },
248 geometry,
249 face_type,
250 boundary_condition: BoundaryCondition::new(bc),
251 apertures: Vec::new(),
252 doors: Vec::new(),
253 }
254 }
255
256 pub fn set_construction(&mut self, construction: String) {
258 self.properties.energy = Some(FaceEnergy {
259 ty: "FaceEnergyPropertiesAbridged",
260 construction: Some(construction),
261 });
262 }
263
264 pub fn set_surface_bc(&mut self, adj_face: String, adj_room: String) {
268 self.boundary_condition = BoundaryCondition::surface(adj_face, adj_room);
269 self.apertures.clear();
270 self.doors.clear();
271 }
272}
273
274#[derive(Serialize)]
276pub struct Room {
277 #[serde(rename = "type")]
278 pub ty: &'static str, pub identifier: String,
280 pub display_name: String,
281 pub properties: TypedProps,
282 pub faces: Vec<Face>,
283}
284
285impl Room {
286 pub fn new(identifier: String, faces: Vec<Face>) -> Self {
287 let display_name = identifier.clone();
288 Self {
289 ty: "Room",
290 identifier,
291 display_name,
292 properties: TypedProps { ty: "RoomPropertiesAbridged" },
293 faces,
294 }
295 }
296}
297
298#[derive(Serialize)]
300pub struct Model {
301 #[serde(rename = "type")]
302 pub ty: &'static str, pub identifier: String,
304 pub display_name: String,
305 pub units: &'static str, pub properties: ModelProperties,
307 pub rooms: Vec<Room>,
308 #[serde(skip_serializing_if = "Vec::is_empty")]
309 pub shade_meshes: Vec<ShadeMesh>,
310 pub tolerance: f64,
311 pub angle_tolerance: f64,
312 pub version: &'static str,
313}
314
315impl Model {
316 pub fn new(
317 identifier: &str,
318 rooms: Vec<Room>,
319 shade_meshes: Vec<ShadeMesh>,
320 energy: Option<ModelEnergy>,
321 tolerance: f64,
322 ) -> Self {
323 Self {
324 ty: "Model",
325 identifier: identifier.to_string(),
326 display_name: identifier.to_string(),
327 units: "Meters",
328 properties: ModelProperties { ty: "ModelProperties", energy },
329 rooms,
330 shade_meshes,
331 tolerance,
332 angle_tolerance: 1.0,
333 version: "1.0.0",
334 }
335 }
336}