rgmsh/
lib.rs

1#![doc(html_logo_url = "https://gitlab.onelab.info/gmsh/gmsh/raw/master/utils/icons/gmsh.svg")]
2#![deny(missing_docs)]
3//!
4//!
5//! Unofficial, opinionated Rust bindings for the Gmsh API.
6//!
7//! From the [Gmsh website](http://gmsh.info/):
8//! > Gmsh is a free 3D finite element mesh generator with a built-in CAD engine and post-processor.
9//!
10//! Gmsh is copyright (C) 1997-2019 by C. Geuzaine and J.-F. Remacle
11//!
12//! The full Gmsh reference manual is available at: [http://gmsh.info/doc/texinfo/gmsh.html](http://gmsh.info/doc/texinfo/gmsh.html)
13//!
14//! ## The Gmsh API
15//!
16//! Gmsh has four main modules:
17//! 1. Geometry
18//! 2. Mesh
19//! 3. Solvers
20//! 4. Post-processing
21//!
22//! ## Rust API design
23//! Gmsh uses tags to keep track of different components. For example, adding a
24//! point to a geometry model will return a `PointTag`.
25//!
26//! Tags are used everywhere in Gmsh, not just for geometry.
27//! Other examples are:
28//! * Mesh elements,
29//! * Post-processing data sets,
30//! * Mesh refinement fields.
31
32// todo figure out where this import belongs
33extern crate gmsh_sys;
34
35use std::os::raw::{c_int, c_char, c_void};
36
37pub mod err;
38#[doc(inline)]
39pub use err::{GmshError, GmshResult};
40
41pub mod fltk;
42
43pub mod interface;
44use std::ffi::{CStr, CString};
45use interface::get_cstring;
46
47pub mod model;
48#[doc(inline)]
49pub use model::{GeoModel, OccModel};
50
51// mes
52struct FieldTag(i64);
53
54// post-processing
55struct ViewTag(i64);
56
57/// Gmsh context object
58pub struct Gmsh {
59    // todo add a log for used-model names
60}
61
62// gmsh {
63//
64//     model {
65//         mesh {
66//             field {}
67//         }
68//         geo {
69//             mesh {}
70//         }
71//         occ {}
72//     }
73//
74//     view {}
75//     plugin {}
76//     options {}
77//     graphics {}
78//     fltk {}
79//     onelab {}
80//     logger {}
81//
82//    }
83
84impl Gmsh {
85    /// Create the main Gmsh object. All API functions are provided through this
86    /// object.
87    // think about std::sync::Once object ?
88    pub fn initialize() -> GmshResult<Self> {
89        // println!("opening Gmsh...");
90
91        unsafe {
92            let mut ierr: c_int = 0;
93            let gmsh_name = CString::new("gmsh").unwrap();
94            let name_arg = gmsh_name.into_raw();
95            gmsh_sys::gmshInitialize(
96                // argc
97                1 as c_int,
98                // argv
99                [name_arg].as_mut_ptr(),
100                // don't read configuration files
101                0,
102                // error out-parameter
103                &mut ierr,
104            );
105
106            // free name_arg
107            let _ = CString::from_raw(name_arg);
108
109            if ierr == 0 {
110                // send logs to terminal
111                let mut gmsh = Self {};
112                gmsh.set_number_option("General.Terminal", 1.)?;
113                //println!("Gmsh {}", gmsh.get_string_option("General.Version")?);
114                Ok(gmsh)
115            } else {
116                Err(GmshError::Initialization)
117            }
118        }
119    }
120
121    /// Make a new model using the built-in Gmsh geometry kernel
122    pub fn create_native_model(&self, name: &'static str) -> GmshResult<GeoModel> {
123        //  println!("added built-in geometry model {} ", name);
124        GeoModel::create(self, name)
125    }
126
127    /// Make a new model using the OpenCASCADE geometry kernel
128    pub fn create_occ_model(&self, name: &'static str) -> GmshResult<OccModel> {
129        //  println!("added OpenCASCADE model {} ", name);
130        OccModel::create(self, name)
131    }
132
133    /// Get a numeric option.
134    pub fn get_number_option(&self, name: &str) -> GmshResult<f64> {
135        let c_name = get_cstring(name)?;
136        let mut value: f64 = 0.;
137        let mut ierr: c_int = 0;
138        unsafe {
139            gmsh_sys::gmshOptionGetNumber(c_name.as_ptr(), &mut value, &mut ierr);
140        }
141        check_option_error!(ierr, value)
142    }
143
144    /// Set a numeric option.
145    pub fn set_number_option(&mut self, name: &str, value: f64) -> GmshResult<()> {
146        let c_name = get_cstring(name)?;
147        let mut ierr: c_int = 0;
148        unsafe {
149            gmsh_sys::gmshOptionSetNumber(c_name.as_ptr(), value, &mut ierr);
150        }
151        check_option_error!(ierr, ())
152    }
153
154    /// Get a string option.
155    pub fn get_string_option(&self, name: &str) -> GmshResult<String> {
156        let c_name = get_cstring(name)?;
157        let mut ierr: c_int = 0;
158        let mut api_val: *mut c_char = &mut 0;
159        unsafe {
160            gmsh_sys::gmshOptionGetString(c_name.as_ptr(), &mut api_val, &mut ierr);
161            // copy string value to Rust UTF8 value
162            let str_val = CStr::from_ptr(api_val as *const c_char).to_str();
163            let ret_val = match str_val {
164                // convert to owned string
165                Ok(val) => check_option_error!(ierr, val.to_string()),
166                Err(_) => Err(GmshError::CInterface),
167            };
168
169        // make sure to only free valid pointers
170	    if *api_val != 0 {
171		    gmsh_sys::gmshFree(api_val as *mut c_void);
172	    }
173
174            ret_val
175        }
176    }
177
178    /// Set a string option.
179    pub fn set_string_option(&mut self, name: &str, value: &str) -> GmshResult<()> {
180        let c_name = get_cstring(name)?;
181        let c_value = get_cstring(value)?;
182        let mut ierr: c_int = 0;
183        unsafe {
184            gmsh_sys::gmshOptionSetString(c_name.as_ptr(), c_value.as_ptr(), &mut ierr);
185        }
186        check_option_error!(ierr, ())
187    }
188}
189
190impl Drop for Gmsh {
191    fn drop(&mut self) {
192        // println!("finalizing Gmsh...");
193        unsafe {
194            // don't check finalization errors
195            let mut ierr: c_int = 0;
196            gmsh_sys::gmshFinalize(&mut ierr);
197        }
198    }
199}
200
201/// Tests must be run with `--test-threads=1` since they depend on the shared Gmsh state.
202#[cfg(test)]
203mod tests {
204
205    // import all names from the outer scope
206    use super::*;
207    use crate::model::*;
208
209    /// Check multiple models can be made and follow the same numbering rules
210    #[test]
211    pub fn multiple_models() -> GmshResult<()> {
212        let gmsh = Gmsh::initialize()?;
213        let mut occ_geom = gmsh.create_occ_model("box")?;
214        let p1 = occ_geom.add_point(0., 0., 0.)?;
215
216        let mut native_geom = gmsh.create_native_model("bella")?;
217        let p2 = native_geom.add_point(1., 1., 1.)?;
218
219        let mut another_native_geom = gmsh.create_native_model("plane")?;
220        let p3 = another_native_geom.add_point(2., 2., 2.)?;
221
222        assert!((p1 == p2) && (p1 == p3));
223        Ok(())
224    }
225
226    #[test]
227    pub fn catch_unknown_options() -> GmshResult<()> {
228        let mut gmsh = Gmsh::initialize()?;
229        let geom = gmsh.create_occ_model("model")?;
230        let bad_opt = "Bad.Option";
231
232        let get_num_err = gmsh.get_number_option(bad_opt);
233        let get_str_err = gmsh.get_string_option(bad_opt);
234        let set_num_err = gmsh.set_number_option(bad_opt, 1.);
235        let set_str_err = gmsh.set_string_option(bad_opt, "Garbo");
236
237        macro_rules! is_unknown_err {
238            ($err:ident) => {
239                match $err {
240                    Err(GmshError::UnknownOption) => (),
241                    _ => panic!(),
242                }
243            };
244        }
245
246        is_unknown_err!(get_num_err);
247        is_unknown_err!(get_str_err);
248        is_unknown_err!(set_num_err);
249        is_unknown_err!(set_str_err);
250
251        Ok(())
252    }
253
254    #[test]
255    pub fn set_and_return_opts() -> GmshResult<()> {
256        let mut gmsh = Gmsh::initialize()?;
257        let geom = gmsh.create_occ_model("model")?;
258
259        let opt = "Solver.Name0";
260        // Solver.Name0 has default value of GetDP
261        let str_val = "TEST_NAME_1";
262        gmsh.set_string_option(opt, str_val)?;
263        assert!(str_val == gmsh.get_string_option(opt)?);
264
265        // has default value of 0
266        gmsh.set_number_option("General.Axes", 5.)?;
267        assert!(5. == gmsh.get_number_option("General.Axes")?);
268
269        Ok(())
270    }
271}