This commit is contained in:
张壹 2025-02-15 21:50:19 +08:00
parent 10c884b155
commit fb4ffd2acd
6 changed files with 456 additions and 685 deletions

View File

@ -26,7 +26,7 @@ add_example(kde_ex OFF)
add_example(meshio_ex OFF) add_example(meshio_ex OFF)
add_example(autodiff_ex OFF) add_example(autodiff_ex OFF)
add_example(multinary_ex OFF) add_example(multinary_ex OFF)
add_example(text_io_ex ON) add_example(text_io_ex OFF)
add_example(getoption_ex OFF) add_example(getoption_ex OFF)
add_example(process_ex OFF) add_example(process_ex OFF)
add_example(array_ex OFF) add_example(array_ex OFF)

View File

@ -34,6 +34,22 @@ int main(int argc, char const *argv[]) try
{ {
mesh_io mshio; mesh_io mshio;
mshio.read_gmsh_v2_ascii("tmp/example2");
mshio.info();
/*
array<vertex2dc> nodes;
array<triangle2d> tris;
//array<vertex3dc*> orinode_ptrs;
//array<meshio_element*> oriele_ptrs;
mshio.select_elements("Survey");
mshio.export_to(tris, nodes);
gmshio tmp_out;
tmp_out.init_file("tmp", Output);
tmp_out.set_packed(NotPacked, Output);
tmp_out.save_mesh(tris, nodes);
*/
/* /*
mshio.read_tetgen_ascii("tmp/ex1.1"); mshio.read_tetgen_ascii("tmp/ex1.1");
mshio.edit_group(Disable, GeometryTag, 5); mshio.edit_group(Disable, GeometryTag, 5);
@ -47,7 +63,7 @@ int main(int argc, char const *argv[]) try
mshio.edit_group(GeometryTag, 4, "Body3"); mshio.edit_group(GeometryTag, 4, "Body3");
mshio.save_gmsh_v2_ascii("tmp/ex1.1"); mshio.save_gmsh_v2_ascii("tmp/ex1.1");
*/ */
/*
mshio.read_gmsh_v2_ascii("tmp/ex1.1"); mshio.read_gmsh_v2_ascii("tmp/ex1.1");
mshio.convert_tags_to_data(GeometryTag); mshio.convert_tags_to_data(GeometryTag);
@ -70,6 +86,7 @@ int main(int argc, char const *argv[]) try
gio.init_file("tmp.msh", Output); gio.init_file("tmp.msh", Output);
gio.set_packed(NotPacked, Output); gio.set_packed(NotPacked, Output);
gio.save_mesh(body2_tets, nodes); gio.save_mesh(body2_tets, nodes);
*/
/* /*
mshio.read_gmsh_v2_ascii("tmp/wjb.1"); mshio.read_gmsh_v2_ascii("tmp/wjb.1");
mshio.edit_group(Disable); mshio.edit_group(Disable);

View File

@ -46,7 +46,7 @@ namespace gctl
}; };
/** /**
* @brief Gmsh文件的IO类 * @brief Gmsh文件的IO类 使mesh_io类型替代
*/ */
class gmshio class gmshio
{ {

View File

@ -254,6 +254,9 @@ gctl::mesh_io::~mesh_io()
void gctl::mesh_io::reset() void gctl::mesh_io::reset()
{ {
if (!selected_nodes_.empty()) selected_nodes_.clear();
if (!selected_elems_.empty()) selected_elems_.clear();
if (!selected_groups_.empty()) selected_groups_.clear();
if (!nodes_.empty()) nodes_.clear(); if (!nodes_.empty()) nodes_.clear();
if (!elems_.empty()) elems_.clear(); if (!elems_.empty()) elems_.clear();
if (!nodes_tag_.empty()) nodes_tag_.clear(); if (!nodes_tag_.empty()) nodes_tag_.clear();
@ -346,33 +349,6 @@ void gctl::mesh_io::info(std::ostream &ss)
return; return;
} }
void gctl::mesh_io::edit_data(switch_type_e swt, std::string dataname)
{
if (dataname == "null" && swt == Enable)
{
for (size_t i = 0; i < datas_.size(); i++)
{
datas_[i].enabled = true;
}
}
else if (dataname == "null" && swt == Disable)
{
for (size_t i = 0; i < datas_.size(); i++)
{
datas_[i].enabled = false;
}
}
else
{
for (size_t i = 0; i < datas_.size(); i++)
{
if (datas_[i].str_tag.front() == dataname && swt == Enable) datas_[i].enabled = true;
if (datas_[i].str_tag.front() == dataname && swt == Disable) datas_[i].enabled = false;
}
}
return;
}
void gctl::mesh_io::edit_group(switch_type_e swt, element_type_enum e_type) void gctl::mesh_io::edit_group(switch_type_e swt, element_type_enum e_type)
{ {
for (size_t g = 0; g < groups_.size(); g++) for (size_t g = 0; g < groups_.size(); g++)
@ -474,48 +450,65 @@ void gctl::mesh_io::edit_group(element_tag_enum anchor_type, int anchor_group, e
return; return;
} }
const gctl::array<int> &gctl::mesh_io::get_node_tag() int gctl::mesh_io::group_tag(element_tag_enum tag_type, std::string group_name)
{
return nodes_tag_;
}
int gctl::mesh_io::get_tag(element_tag_enum anchor_type, std::string anchor_name)
{ {
for (size_t i = 0; i < groups_.size(); i++) for (size_t i = 0; i < groups_.size(); i++)
{ {
if (anchor_type == PhysicalTag && groups_[i].name == anchor_name) return groups_[i].phys_group; if (tag_type == PhysicalTag && groups_[i].name == group_name) return groups_[i].phys_group;
if (anchor_type == GeometryTag && groups_[i].name == anchor_name) return groups_[i].geom_group; if (tag_type == GeometryTag && groups_[i].name == group_name) return groups_[i].geom_group;
if (anchor_type == PartitionTag && groups_[i].name == anchor_name) return groups_[i].part_group; if (tag_type == PartitionTag && groups_[i].name == group_name) return groups_[i].part_group;
} }
return DEFAULT_INVALID_TAG; return DEFAULT_INVALID_TAG;
} }
const gctl::array<gctl::vertex3dc> &gctl::mesh_io::get_nodes() const gctl::array<gctl::vertex3dc> &gctl::mesh_io::get_all_nodes()
{ {
return nodes_; return nodes_;
} }
const gctl::array<gctl::meshio_element> &gctl::mesh_io::get_elems() const gctl::array<gctl::meshio_element> &gctl::mesh_io::get_all_elems()
{ {
return elems_; return elems_;
} }
size_t gctl::mesh_io::element_size(element_type_enum e_type) void gctl::mesh_io::select_elements(element_type_enum e_type)
{ {
size_t s = 0; if (!selected_nodes_.empty()) selected_nodes_.clear();
if (!selected_elems_.empty()) selected_elems_.clear();
if (!selected_groups_.empty()) selected_groups_.clear();
int vnum;
for (size_t i = 0; i < groups_.size(); i++) for (size_t i = 0; i < groups_.size(); i++)
{ {
if ((e_type == NotSet || groups_[i].type == e_type) && groups_[i].enabled) if ((e_type == NotSet || groups_[i].type == e_type) && groups_[i].enabled)
{ {
s += groups_[i].elem_ptrs.size(); selected_groups_.push_back(&groups_[i]);
vnum = elem_size(groups_[i].type);
for (size_t e = 0; e < groups_[i].elem_ptrs.size(); e++)
{
selected_elems_.push_back(groups_[i].elem_ptrs[e]);
for (size_t v = 0; v < vnum; v++)
{
selected_nodes_.push_back(groups_[i].elem_ptrs[e]->vert_ptrs[v]);
}
}
} }
} }
return s;
std::vector<vertex3dc*>::iterator pos;
std::sort(selected_nodes_.begin(), selected_nodes_.end()); //排序
pos = std::unique(selected_nodes_.begin(), selected_nodes_.end()); //获取重复序列开始的位置
selected_nodes_.erase(pos, selected_nodes_.end()); //删除重复点
return;
} }
size_t gctl::mesh_io::element_size(element_tag_enum tag_type, int tag) void gctl::mesh_io::select_elements(element_tag_enum tag_type, int tag)
{ {
size_t s = 0; if (!selected_nodes_.empty()) selected_nodes_.clear();
if (!selected_elems_.empty()) selected_elems_.clear();
if (!selected_groups_.empty()) selected_groups_.clear();
int vnum;
for (size_t i = 0; i < groups_.size(); i++) for (size_t i = 0; i < groups_.size(); i++)
{ {
if ((tag_type == PhysicalTag && groups_[i].phys_group == tag) || if ((tag_type == PhysicalTag && groups_[i].phys_group == tag) ||
@ -523,23 +516,134 @@ size_t gctl::mesh_io::element_size(element_tag_enum tag_type, int tag)
(tag_type == PartitionTag && groups_[i].part_group == tag) && (tag_type == PartitionTag && groups_[i].part_group == tag) &&
groups_[i].enabled) groups_[i].enabled)
{ {
s += groups_[i].elem_ptrs.size(); selected_groups_.push_back(&groups_[i]);
vnum = elem_size(groups_[i].type);
for (size_t e = 0; e < groups_[i].elem_ptrs.size(); e++)
{
selected_elems_.push_back(groups_[i].elem_ptrs[e]);
for (size_t v = 0; v < vnum; v++)
{
selected_nodes_.push_back(groups_[i].elem_ptrs[e]->vert_ptrs[v]);
}
}
} }
} }
return s;
std::vector<vertex3dc*>::iterator pos;
std::sort(selected_nodes_.begin(), selected_nodes_.end()); //排序
pos = std::unique(selected_nodes_.begin(), selected_nodes_.end()); //获取重复序列开始的位置
selected_nodes_.erase(pos, selected_nodes_.end()); //删除重复点
return;
} }
size_t gctl::mesh_io::element_size(std::string phys_name) void gctl::mesh_io::select_elements(std::string phys_name)
{ {
size_t s = 0; if (!selected_nodes_.empty()) selected_nodes_.clear();
if (!selected_elems_.empty()) selected_elems_.clear();
if (!selected_groups_.empty()) selected_groups_.clear();
int vnum;
for (size_t i = 0; i < groups_.size(); i++) for (size_t i = 0; i < groups_.size(); i++)
{ {
if (groups_[i].enabled && groups_[i].name == phys_name) if (groups_[i].enabled && groups_[i].name == phys_name)
{ {
s += groups_[i].elem_ptrs.size(); selected_groups_.push_back(&groups_[i]);
vnum = elem_size(groups_[i].type);
for (size_t e = 0; e < groups_[i].elem_ptrs.size(); e++)
{
selected_elems_.push_back(groups_[i].elem_ptrs[e]);
for (size_t v = 0; v < vnum; v++)
{
selected_nodes_.push_back(groups_[i].elem_ptrs[e]->vert_ptrs[v]);
}
}
} }
} }
return s;
std::vector<vertex3dc*>::iterator pos;
std::sort(selected_nodes_.begin(), selected_nodes_.end()); //排序
pos = std::unique(selected_nodes_.begin(), selected_nodes_.end()); //获取重复序列开始的位置
selected_nodes_.erase(pos, selected_nodes_.end()); //删除重复点
return;
}
size_t gctl::mesh_io::node_size()
{
return selected_nodes_.size();
}
size_t gctl::mesh_io::element_size()
{
return selected_elems_.size();
}
const gctl::array<int> &gctl::mesh_io::node_tags()
{
selected_node_tag_.resize(selected_nodes_.size());
for (size_t i = 0; i < selected_node_tag_.size(); i++)
{
selected_node_tag_[i] = nodes_tag_[selected_nodes_[i]->id];
}
return selected_node_tag_;
}
const gctl::array<int> &gctl::mesh_io::element_tags(element_tag_enum tag_type)
{
selected_elem_tag_.resize(selected_elems_.size());
size_t id = 0;
for (size_t i = 0; i < selected_groups_.size(); i++)
{
for (size_t e = 0; e < selected_groups_[i]->elem_ptrs.size(); e++)
{
if (tag_type == PhysicalTag) selected_elem_tag_[id] = selected_groups_[i]->phys_group;
else if (tag_type == GeometryTag) selected_elem_tag_[id] = selected_groups_[i]->geom_group;
else if (tag_type == PartitionTag) selected_elem_tag_[id] = selected_groups_[i]->part_group;
else selected_elem_tag_[id] = DEFAULT_INVALID_TAG;
id++;
}
}
return selected_elem_tag_;
}
void gctl::mesh_io::get_gmsh_physical_groups(std::vector<gmsh_physical_group> &g_groups)
{
if (!g_groups.empty()) g_groups.clear();
gmsh_physical_group tmp_group;
bool not_found;
for (size_t i = 0; i < groups_.size(); i++)
{
if (g_groups.empty() && groups_[i].enabled)
{
tmp_group.name = groups_[i].name;
tmp_group.phys_tag = groups_[i].phys_group;
tmp_group.dim_tag = groups_[i].part_group;
g_groups.push_back(tmp_group);
}
else
{
not_found = true;
for (size_t g = 0; g < g_groups.size(); g++)
{
if (groups_[i].part_group == g_groups[g].dim_tag &&
groups_[i].phys_group == g_groups[g].phys_tag)
{
not_found = false;
break;
}
}
if (not_found && groups_[i].enabled)
{
tmp_group.name = groups_[i].name;
tmp_group.phys_tag = groups_[i].phys_group;
tmp_group.dim_tag = groups_[i].part_group;
g_groups.push_back(tmp_group);
}
}
}
return;
} }
void gctl::mesh_io::convert_tags_to_data(element_tag_enum tag_type) void gctl::mesh_io::convert_tags_to_data(element_tag_enum tag_type)
@ -609,188 +713,6 @@ void gctl::mesh_io::convert_tags_to_data(element_tag_enum tag_type)
return; return;
} }
void gctl::mesh_io::export_elements_to(array<triangle> &tris, std::string phys_name)
{
size_t s = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].type == _3NodeTriangle &&
(groups_[i].name == phys_name || phys_name == "All"))
{
s += groups_[i].elem_ptrs.size();
}
}
tris.resize(s);
s = 0;
for (size_t i = 0; i < elems_.size(); i++)
{
if (groups_[i].enabled && groups_[i].type == _3NodeTriangle &&
(groups_[i].name == phys_name || phys_name == "All"))
{
for (size_t e = 0; e < groups_[i].elem_ptrs.size(); e++)
{
tris[s].id = groups_[i].elem_ptrs[e]->id;
tris[s].vert[0] = groups_[i].elem_ptrs[e]->vert_ptrs[0];
tris[s].vert[1] = groups_[i].elem_ptrs[e]->vert_ptrs[1];
tris[s].vert[2] = groups_[i].elem_ptrs[e]->vert_ptrs[2];
s++;
}
}
}
return;
}
void gctl::mesh_io::export_elements_to(array<triangle> &tris, element_tag_enum tag_type, int tag)
{
size_t s = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].type == _3NodeTriangle &&
((tag_type == PhysicalTag && groups_[i].phys_group == tag) ||
(tag_type == GeometryTag && groups_[i].geom_group == tag) ||
(tag_type == PartitionTag && groups_[i].part_group == tag)))
{
s += groups_[i].elem_ptrs.size();
}
}
tris.resize(s);
s = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].type == _3NodeTriangle &&
((tag_type == PhysicalTag && groups_[i].phys_group == tag) ||
(tag_type == GeometryTag && groups_[i].geom_group == tag) ||
(tag_type == PartitionTag && groups_[i].part_group == tag)))
{
for (size_t e = 0; e < groups_[i].elem_ptrs.size(); e++)
{
tris[s].id = groups_[i].elem_ptrs[e]->id;
tris[s].vert[0] = groups_[i].elem_ptrs[e]->vert_ptrs[0];
tris[s].vert[1] = groups_[i].elem_ptrs[e]->vert_ptrs[1];
tris[s].vert[2] = groups_[i].elem_ptrs[e]->vert_ptrs[2];
s++;
}
}
}
return;
}
void gctl::mesh_io::export_elements_to(array<tetrahedron> &tets, std::string phys_name)
{
size_t s = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].type == _4NodeTetrahedron &&
(groups_[i].name == phys_name || phys_name == "All"))
{
s += groups_[i].elem_ptrs.size();
}
}
tets.resize(s);
s = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].type == _4NodeTetrahedron &&
(groups_[i].name == phys_name || phys_name == "All"))
{
for (size_t e = 0; e < groups_[i].elem_ptrs.size(); e++)
{
tets[s].set(groups_[i].elem_ptrs[e]->vert_ptrs[0],
groups_[i].elem_ptrs[e]->vert_ptrs[1],
groups_[i].elem_ptrs[e]->vert_ptrs[2],
groups_[i].elem_ptrs[e]->vert_ptrs[3],
groups_[i].elem_ptrs[e]->id);
s++;
}
}
}
return;
}
void gctl::mesh_io::export_elements_to(array<tetrahedron> &tets, element_tag_enum tag_type, int tag)
{
size_t s = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].type == _4NodeTetrahedron &&
(tag_type == PhysicalTag && groups_[i].phys_group == tag) ||
(tag_type == GeometryTag && groups_[i].geom_group == tag) ||
(tag_type == PartitionTag && groups_[i].part_group == tag))
{
s += groups_[i].elem_ptrs.size();
}
}
tets.resize(s);
s = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].type == _4NodeTetrahedron &&
(tag_type == PhysicalTag && groups_[i].phys_group == tag) ||
(tag_type == GeometryTag && groups_[i].geom_group == tag) ||
(tag_type == PartitionTag && groups_[i].part_group == tag))
{
for (size_t e = 0; e < groups_[i].elem_ptrs.size(); e++)
{
tets[s].set(groups_[i].elem_ptrs[e]->vert_ptrs[0],
groups_[i].elem_ptrs[e]->vert_ptrs[1],
groups_[i].elem_ptrs[e]->vert_ptrs[2],
groups_[i].elem_ptrs[e]->vert_ptrs[3],
groups_[i].elem_ptrs[e]->id);
s++;
}
}
}
return;
}
void gctl::mesh_io::get_gmsh_physical_groups(std::vector<gmsh_physical_group> &g_groups)
{
if (!g_groups.empty()) g_groups.clear();
gmsh_physical_group tmp_group;
bool not_found;
for (size_t i = 0; i < groups_.size(); i++)
{
if (g_groups.empty() && groups_[i].enabled)
{
tmp_group.name = groups_[i].name;
tmp_group.phys_tag = groups_[i].phys_group;
tmp_group.dim_tag = groups_[i].part_group;
g_groups.push_back(tmp_group);
}
else
{
not_found = true;
for (size_t g = 0; g < g_groups.size(); g++)
{
if (groups_[i].part_group == g_groups[g].dim_tag &&
groups_[i].phys_group == g_groups[g].phys_tag)
{
not_found = false;
break;
}
}
if (not_found && groups_[i].enabled)
{
tmp_group.name = groups_[i].name;
tmp_group.phys_tag = groups_[i].phys_group;
tmp_group.dim_tag = groups_[i].part_group;
g_groups.push_back(tmp_group);
}
}
}
return;
}
int gctl::mesh_io::if_saved_data(std::string name, mesh_data_type_e type) int gctl::mesh_io::if_saved_data(std::string name, mesh_data_type_e type)
{ {
for (size_t i = 0; i < datas_.size(); i++) for (size_t i = 0; i < datas_.size(); i++)
@ -815,92 +737,94 @@ gctl::meshio_data *gctl::mesh_io::get_data_ptr(std::string name, mesh_data_type_
return &datas_[id]; return &datas_[id];
} }
void gctl::mesh_io::add_node_data(std::string name, const array<double> &data) void gctl::mesh_io::add_data(std::string name, const array<double> &data,
mesh_data_type_e dtype, output_type_e op)
{ {
size_t s = nodes_.size(); size_t s;
if (data.size()!= s) throw std::runtime_error("[gctl::mesh_io::create_node_data] Incompatible data size."); if (dtype == NodeData) s = selected_nodes_.size();
else s = selected_elems_.size();
int d_id = if_saved_data(name, NodeData); if (data.size()!= s) throw std::runtime_error("[gctl::mesh_io::add_data] Incompatible data size to the selected nodes or elements.");
if (d_id != -1)
int d_id;
if (dtype == NodeData) d_id = if_saved_data(name, NodeData);
else d_id = if_saved_data(name, ElemData);
if (d_id != -1) // data exist
{
if (op == Append)
{
array<void*> tmp_ptrs(s, nullptr);
if (dtype == NodeData)
{
for (size_t i = 0; i < s; i++)
{
tmp_ptrs[i] = selected_nodes_[i];
}
}
else
{
for (size_t i = 0; i < s; i++)
{
tmp_ptrs[i] = selected_elems_[i];
}
}
datas_[d_id].int_tag[2] += s;
datas_[d_id].tar_ptrs.concat(tmp_ptrs);
datas_[d_id].val.concat(data);
}
else // OverWrite
{
datas_[d_id].int_tag[2] = s;
datas_[d_id].tar_ptrs.resize(s, nullptr);
datas_[d_id].val.resize(s, 0.0);
if (dtype == NodeData)
{
for (size_t i = 0; i < s; i++)
{
datas_[d_id].tar_ptrs[i] = selected_nodes_[i];
datas_[d_id].val[i] = data[i];
}
}
else
{
for (size_t i = 0; i < s; i++)
{
datas_[d_id].tar_ptrs[i] = selected_elems_[i];
datas_[d_id].val[i] = data[i];
}
}
}
return;
}
meshio_data new_data;
new_data.enabled = true;
new_data.d_type = NodeData;
new_data.str_tag.resize(1, name);
new_data.real_tag.resize(1, 0.0);
new_data.int_tag.resize(3, 0);
new_data.int_tag[1] = 1;
new_data.int_tag[2] = s;
new_data.tar_ptrs.resize(s, nullptr);
new_data.val.resize(s);
if (dtype == NodeData)
{ {
for (size_t i = 0; i < s; i++) for (size_t i = 0; i < s; i++)
{ {
datas_[d_id].val[i] = data[i]; new_data.tar_ptrs[i] = selected_nodes_[i];
new_data.val[i] = data[i];
} }
return;
} }
else
meshio_data new_data;
new_data.enabled = true;
new_data.d_type = NodeData;
new_data.str_tag.resize(1, name);
new_data.real_tag.resize(1, 0.0);
new_data.int_tag.resize(3, 0);
new_data.int_tag[1] = 1;
new_data.int_tag[2] = s;
new_data.tar_ptrs.resize(s, nullptr);
new_data.val.resize(s);
for (size_t i = 0; i < s; i++)
{ {
new_data.tar_ptrs[i] = nodes_.get(i); for (size_t i = 0; i < s; i++)
new_data.val[i] = data[i];
}
datas_.push_back(new_data);
return;
}
void gctl::mesh_io::add_node_data(std::string name, const array<double> &data, const array<bool> &boolen)
{
size_t s = nodes_.size();
if (data.size()!= s || boolen.size() != s) throw std::runtime_error("[gctl::mesh_io::create_node_data] Incompatible data size.");
s = 0;
for (size_t i = 0; i < nodes_.size(); i++)
{
if (boolen[i]) s++;
}
int d_id = if_saved_data(name, NodeData);
if (d_id != -1)
{
datas_[d_id].val.resize(s);
datas_[d_id].tar_ptrs.resize(s, nullptr);
datas_[d_id].int_tag[2] = s;
s = 0;
for (size_t i = 0; i < nodes_.size(); i++)
{ {
if (boolen[i]) new_data.tar_ptrs[i] = selected_elems_[i];
{ new_data.val[i] = data[i];
datas_[d_id].tar_ptrs[s] = nodes_.get(i);
datas_[d_id].val[s] = data[i];
s++;
}
}
return;
}
meshio_data new_data;
new_data.enabled = true;
new_data.d_type = NodeData;
new_data.str_tag.resize(1, name);
new_data.real_tag.resize(1, 0.0);
new_data.int_tag.resize(3, 0);
new_data.int_tag[1] = 1;
new_data.int_tag[2] = s;
new_data.tar_ptrs.resize(s, nullptr);
new_data.val.resize(s);
s = 0;
for (size_t i = 0; i < nodes_.size(); i++)
{
if (boolen[i])
{
new_data.tar_ptrs[s] = nodes_.get(i);
new_data.val[s] = data[i];
s++;
} }
} }
@ -908,276 +832,30 @@ void gctl::mesh_io::add_node_data(std::string name, const array<double> &data, c
return; return;
} }
void gctl::mesh_io::add_element_data(std::string name, const array<double> &data, element_type_enum e_type) void gctl::mesh_io::edit_data(switch_type_e swt, std::string dataname)
{ {
size_t e = 0; if (dataname == "null" && swt == Enable)
for (size_t i = 0; i < groups_.size(); i++)
{ {
if ((e_type == NotSet || groups_[i].type == e_type) && groups_[i].enabled) e += groups_[i].elem_ptrs.size(); for (size_t i = 0; i < datas_.size(); i++)
}
if (data.size() != e) throw std::runtime_error("[gctl::mesh_io::create_element_data] Incompatible data size.");
int d_id = if_saved_data(name, ElemData);
if (d_id != -1)
{
datas_[d_id].int_tag[2] = e;
datas_[d_id].tar_ptrs.resize(e, nullptr);
datas_[d_id].val.resize(e, 0.0);
e = 0;
for (size_t i = 0; i < groups_.size(); i++)
{ {
if ((e_type == NotSet || groups_[i].type == e_type) && groups_[i].enabled) datas_[i].enabled = true;
{
for (size_t j = 0; j < groups_[i].elem_ptrs.size(); j++)
{
datas_[d_id].tar_ptrs[e] = groups_[i].elem_ptrs[j];
datas_[d_id].val[e] = data[e];
e++;
}
}
}
return;
}
meshio_data new_data;
new_data.enabled = true;
new_data.d_type = ElemData;
new_data.str_tag.resize(1, name);
new_data.real_tag.resize(1, 0.0);
new_data.int_tag.resize(3, 0);
new_data.int_tag[1] = 1;
new_data.int_tag[2] = e;
new_data.tar_ptrs.resize(e, nullptr);
new_data.val.resize(e, 0.0);
e = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if ((e_type == NotSet || groups_[i].type == e_type) && groups_[i].enabled)
{
for (size_t j = 0; j < groups_[i].elem_ptrs.size(); j++)
{
new_data.tar_ptrs[e] = groups_[i].elem_ptrs[j];
new_data.val[e] = data[e];
e++;
}
} }
} }
else if (dataname == "null" && swt == Disable)
datas_.push_back(new_data);
return;
}
void gctl::mesh_io::add_element_data(std::string name, const array<double> &data, element_tag_enum tag_type, int tag)
{
size_t e = 0;
for (size_t i = 0; i < groups_.size(); i++)
{ {
if ((tag_type == PhysicalTag && groups_[i].phys_group == tag) || for (size_t i = 0; i < datas_.size(); i++)
(tag_type == GeometryTag && groups_[i].geom_group == tag) ||
(tag_type == PartitionTag && groups_[i].part_group == tag) &&
groups_[i].enabled)
{ {
e += groups_[i].elem_ptrs.size(); datas_[i].enabled = false;
} }
} }
else
if (data.size() != e) throw std::runtime_error("[gctl::mesh_io::create_element_data] Incompatible data size.");
int d_id = if_saved_data(name, ElemData);
if (d_id != -1)
{ {
array<void*> more_elem_ptrs(e, nullptr); for (size_t i = 0; i < datas_.size(); i++)
array<double> more_val(e, 0.0);
e = 0;
for (size_t i = 0; i < groups_.size(); i++)
{ {
if ((tag_type == PhysicalTag && groups_[i].phys_group == tag) || if (datas_[i].str_tag.front() == dataname && swt == Enable) datas_[i].enabled = true;
(tag_type == GeometryTag && groups_[i].geom_group == tag) || if (datas_[i].str_tag.front() == dataname && swt == Disable) datas_[i].enabled = false;
(tag_type == PartitionTag && groups_[i].part_group == tag) &&
groups_[i].enabled)
{
for (size_t j = 0; j < groups_[i].elem_ptrs.size(); j++)
{
more_elem_ptrs[e] = groups_[i].elem_ptrs[j];
more_val[e] = data[e];
e++;
}
}
}
datas_[d_id].int_tag[2] += e;
datas_[d_id].tar_ptrs.concat(more_elem_ptrs);
datas_[d_id].val.concat(more_val);
return;
}
meshio_data new_data;
new_data.enabled = true;
new_data.d_type = ElemData;
new_data.str_tag.resize(1, name);
new_data.real_tag.resize(1, 0.0);
new_data.int_tag.resize(3, 0);
new_data.int_tag[1] = 1;
new_data.int_tag[2] = e;
new_data.tar_ptrs.resize(e, nullptr);
new_data.val.resize(e, 0.0);
e = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if ((tag_type == PhysicalTag && groups_[i].phys_group == tag) ||
(tag_type == GeometryTag && groups_[i].geom_group == tag) ||
(tag_type == PartitionTag && groups_[i].part_group == tag) &&
groups_[i].enabled)
{
for (size_t j = 0; j < groups_[i].elem_ptrs.size(); j++)
{
new_data.tar_ptrs[e] = groups_[i].elem_ptrs[j];
new_data.val[e] = data[e];
e++;
}
} }
} }
datas_.push_back(new_data);
return;
}
void gctl::mesh_io::add_element_data(std::string name, std::string phys_name, const array<double> &data)
{
size_t e = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].name == phys_name)
{
e += groups_[i].elem_ptrs.size();
}
}
if (data.size() != e) throw std::runtime_error("[gctl::mesh_io::create_element_data] Incompatible data size.");
int d_id = if_saved_data(name, ElemData);
if (d_id != -1)
{
array<void*> more_elem_ptrs(e, nullptr);
array<double> more_val(e, 0.0);
e = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].name == phys_name)
{
for (size_t j = 0; j < groups_[i].elem_ptrs.size(); j++)
{
more_elem_ptrs[e] = groups_[i].elem_ptrs[j];
more_val[e] = data[e];
e++;
}
}
}
datas_[d_id].int_tag[2] += e;
datas_[d_id].tar_ptrs.concat(more_elem_ptrs);
datas_[d_id].val.concat(more_val);
return;
}
meshio_data new_data;
new_data.enabled = true;
new_data.d_type = ElemData;
new_data.str_tag.resize(1, name);
new_data.real_tag.resize(1, 0.0);
new_data.int_tag.resize(3, 0);
new_data.int_tag[1] = 1;
new_data.int_tag[2] = e;
new_data.tar_ptrs.resize(e, nullptr);
new_data.val.resize(e, 0.0);
e = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].name == phys_name)
{
for (size_t j = 0; j < groups_[i].elem_ptrs.size(); j++)
{
new_data.tar_ptrs[e] = groups_[i].elem_ptrs[j];
new_data.val[e] = data[e];
e++;
}
}
}
datas_.push_back(new_data);
return;
}
void gctl::mesh_io::add_element_data(std::string name, std::string phys_name, double phys_val)
{
size_t e = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].name == phys_name)
{
e += groups_[i].elem_ptrs.size();
}
}
int d_id = if_saved_data(name, ElemData);
if (d_id != -1)
{
array<void*> more_elem_ptrs(e, nullptr);
array<double> more_val(e, 0.0);
e = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].name == phys_name)
{
for (size_t j = 0; j < groups_[i].elem_ptrs.size(); j++)
{
more_elem_ptrs[e] = groups_[i].elem_ptrs[j];
more_val[e] = phys_val;
e++;
}
}
}
datas_[d_id].int_tag[2] += e;
datas_[d_id].tar_ptrs.concat(more_elem_ptrs);
datas_[d_id].val.concat(more_val);
return;
}
meshio_data new_data;
new_data.enabled = true;
new_data.d_type = ElemData;
new_data.str_tag.resize(1, name);
new_data.real_tag.resize(1, 0.0);
new_data.int_tag.resize(3, 0);
new_data.int_tag[1] = 1;
new_data.int_tag[2] = e;
new_data.tar_ptrs.resize(e, nullptr);
new_data.val.resize(e, 0.0);
e = 0;
for (size_t i = 0; i < groups_.size(); i++)
{
if (groups_[i].enabled && groups_[i].name == phys_name)
{
for (size_t j = 0; j < groups_[i].elem_ptrs.size(); j++)
{
new_data.tar_ptrs[e] = groups_[i].elem_ptrs[j];
new_data.val[e] = phys_val;
e++;
}
}
}
datas_.push_back(new_data);
return; return;
} }
@ -2175,3 +1853,31 @@ void gctl::mesh_io::sort_groups()
destroy_vector(tmp_groups); destroy_vector(tmp_groups);
return; return;
} }
gctl::element_type_enum gctl::mesh_io::match_type(const std::type_info &tinfo)
{
std::smatch ret;
std::regex pat_triangle("type_triangle");
std::regex pat_tetrahedron("type_tetrahedron");
std::regex pat_block("type_block");
std::regex pat_edge("type_edge");
std::regex pat_edge2d("type_edge2d");
std::regex pat_node("type_node");
std::regex pat_prism("type_prism");
std::regex pat_rectangle2d("type_rectangle2d");
std::regex pat_tricone("type_tricone");
std::regex pat_triangle2d("type_triangle2d");
std::string t_str = tinfo.name();
if (regex_search(t_str, ret, pat_triangle)) return _3NodeTriangle;
else if (regex_search(t_str, ret, pat_tetrahedron)) return _4NodeTetrahedron;
else if (regex_search(t_str, ret, pat_block)) return _8NodeHexahedron;
else if (regex_search(t_str, ret, pat_edge)) return _2NodeLine;
else if (regex_search(t_str, ret, pat_edge2d)) return _2NodeLine;
else if (regex_search(t_str, ret, pat_node)) return _1NodePoint;
else if (regex_search(t_str, ret, pat_prism)) return _6NodePrism;
else if (regex_search(t_str, ret, pat_rectangle2d)) return _4NodeQuadrangle;
else if (regex_search(t_str, ret, pat_tricone)) return _4NodeTetrahedron;
else if (regex_search(t_str, ret, pat_triangle2d)) return _3NodeTriangle;
else return NotSet;
}

View File

@ -194,14 +194,6 @@ namespace gctl
*/ */
void info(std::ostream &ss = std::clog); void info(std::ostream &ss = std::clog);
/**
* @brief
*
* @param swt 使Enable或Disable
* @param dataname null
*/
void edit_data(switch_type_e swt, std::string dataname = "null");
/** /**
* @brief * @brief
* *
@ -246,101 +238,78 @@ namespace gctl
*/ */
void edit_group(element_tag_enum anchor_type, int anchor_group, element_tag_enum tar_type, int tar_group); void edit_group(element_tag_enum anchor_type, int anchor_group, element_tag_enum tar_type, int tar_group);
/**
* @brief
*
* @return
*/
const array<int> &get_node_tag();
/** /**
* @brief * @brief
* *
* @param anchor_type PhysicalTagGeometryTag或者PartitionTag * @param tag_type PhysicalTagGeometryTag或者PartitionTag
* @param anchor_name * @param group_name
* @return * @return
*/ */
int get_tag(element_tag_enum anchor_type, std::string anchor_name); int group_tag(element_tag_enum tag_type, std::string group_name);
/** /**
* @brief * @brief
* *
* @return * @return
*/ */
const array<vertex3dc> &get_nodes(); const array<vertex3dc> &get_all_nodes();
/** /**
* @brief * @brief
* *
* @return * @return
*/ */
const array<meshio_element> &get_elems(); const array<meshio_element> &get_all_elems();
/** /**
* @brief * @brief
* *
* @param e_type NotSet * @param e_type NotSet
* @return
*/ */
size_t element_size(element_type_enum e_type = NotSet); void select_elements(element_type_enum e_type = NotSet);
/** /**
* @brief * @brief
* *
* @param tag_type * @param tag_type
* @param tag * @param tag
* @return
*/ */
size_t element_size(element_tag_enum tag_type, int tag); void select_elements(element_tag_enum tag_type, int tag);
/** /**
* @brief * @brief
* *
* @param phys_name * @param phys_name
* @return
*/ */
size_t element_size(std::string phys_name); void select_elements(std::string phys_name);
/** /**
* @brief * @brief 使select_elements函数选择
* *
* @param tag_type * @return
*/ */
void convert_tags_to_data(element_tag_enum tag_type); size_t node_size();
/** /**
* @brief * @brief 使select_elements函数选择
* *
* @param tris * @return
* @param phys_name
*/ */
void export_elements_to(array<triangle> &tris, std::string phys_name = "All"); size_t element_size();
/** /**
* @brief * @brief
* *
* @param tris * @return
* @param tag_type
* @param tag
*/ */
void export_elements_to(array<triangle> &tris, element_tag_enum tag_type, int tag); const array<int> &node_tags();
/** /**
* @brief * @brief
* *
* @param tets * @return
* @param phys_name
*/ */
void export_elements_to(array<tetrahedron> &tets, std::string phys_name = "All"); const array<int> &element_tags(element_tag_enum tag_type);
/**
* @brief
*
* @param tris
* @param tag_type
* @param tag
*/
void export_elements_to(array<tetrahedron> &tets, element_tag_enum tag_type, int tag);
/** /**
* @brief gmsh格式分组表 * @brief gmsh格式分组表
@ -349,6 +318,13 @@ namespace gctl
*/ */
void get_gmsh_physical_groups(std::vector<gmsh_physical_group> &g_groups); void get_gmsh_physical_groups(std::vector<gmsh_physical_group> &g_groups);
/**
* @brief
*
* @param tag_type
*/
void convert_tags_to_data(element_tag_enum tag_type);
/** /**
* @brief name的数据 * @brief name的数据
* *
@ -378,70 +354,23 @@ namespace gctl
meshio_data *get_data_ptr(std::string name, mesh_data_type_e type); meshio_data *get_data_ptr(std::string name, mesh_data_type_e type);
/** /**
* @brief * @brief
* *
* @note * @param data
*
* @param data
* @param name * @param name
* @param dtype
* @param op OverWrite或Append
*/ */
void add_node_data(std::string name, const array<double> &data); void add_data(std::string name, const array<double> &data,
mesh_data_type_e dtype, output_type_e op = OverWrite);
/** /**
* @brief * @brief
* *
* @note * @param swt 使Enable或Disable
* * @param dataname null
* @param data
* @param boolen
* @param name
*/ */
void add_node_data(std::string name, const array<double> &data, const array<bool> &boolen); void edit_data(switch_type_e swt, std::string dataname = "null");
/**
* @brief
*
* @note
*
* @param data
* @param name
* @param e_type NotSet
*/
void add_element_data(std::string name, const array<double> &data, element_type_enum e_type = NotSet);
/**
* @brief
*
* @note
*
* @param data
* @param name
* @param tag_type
* @param tag
*/
void add_element_data(std::string name, const array<double> &data, element_tag_enum tag_type, int tag);
/**
* @brief
*
* @note
*
* @param data
* @param name
* @param phys_name
*/
void add_element_data(std::string name, std::string phys_name, const array<double> &data);
/**
* @brief
*
* @note
*
* @param phys_val
* @param name
* @param phys_name
*/
void add_element_data(std::string name, std::string phys_name, double phys_val);
/** /**
* @brief triangle软件输出的网格剖分文件 * @brief triangle软件输出的网格剖分文件
@ -493,6 +422,31 @@ namespace gctl
*/ */
void save_data_to_xyz(std::string filename, std::string dataname = "null", coordinate_system_e out_coor = Cartesian, double refr = GCTL_Earth_Radius, double refR = GCTL_Earth_Radius); void save_data_to_xyz(std::string filename, std::string dataname = "null", coordinate_system_e out_coor = Cartesian, double refr = GCTL_Earth_Radius, double refR = GCTL_Earth_Radius);
/**
* @brief
*
* @tparam T
* @param elems
* @param nodes
*/
template <typename T>
void export_to(array<T> &elems, array<vertex3dc> &nodes);
/**
* @brief
*
* @tparam T
* @param elems
* @param nodes
* @param x_id x索引0
* @param y_id y索引1
*
* @note xyz坐标中的对应项分别赋值给二维坐标的xy值{0,1}xoy平面
* {1,0}yox平面{0,2}xoz平面{1,2}yoz平面
*/
template <typename T>
void export_to(array<T> &elems, array<vertex2dc> &nodes, int x_id = 0, int y_id = 1);
protected: protected:
bool initialized_; // 类型是否已经初始化完成 bool initialized_; // 类型是否已经初始化完成
@ -504,6 +458,12 @@ namespace gctl
std::vector<meshio_element_group> groups_; // 网格单元体组 std::vector<meshio_element_group> groups_; // 网格单元体组
array<int> nodes_tag_; // 顶点标签 array<int> nodes_tag_; // 顶点标签
array<int> selected_node_tag_; // 被选择的顶点的标签
array<int> selected_elem_tag_; // 被选择的单元体的标签
std::vector<vertex3dc*> selected_nodes_; // 被选择的顶点
std::vector<meshio_element*> selected_elems_; // 被选择的单元体
std::vector<meshio_element_group*> selected_groups_; // 被选择的单元体组
element_type_enum elem_gmshcode2type_[94]; // gmsh的单元体类型数组 数组索引为gmsh的单元体类型码值 element_type_enum elem_gmshcode2type_[94]; // gmsh的单元体类型数组 数组索引为gmsh的单元体类型码值
element_type_enum elem_vtkcode2type_[14]; // vtk的单元体类型数组 数组索引为vtk的单元体类型码值 element_type_enum elem_vtkcode2type_[14]; // vtk的单元体类型数组 数组索引为vtk的单元体类型码值
std::map<element_type_enum, int> elem_type2gmshcode_; // 单元体类型到gmsh类型码值的映射 std::map<element_type_enum, int> elem_type2gmshcode_; // 单元体类型到gmsh类型码值的映射
@ -519,7 +479,84 @@ namespace gctl
element_type_enum elem_vtk_type(int code); // 获取对应vtk类型码的单元体类型 element_type_enum elem_vtk_type(int code); // 获取对应vtk类型码的单元体类型
void update_indexing(); // 更新索引(对网格的元素进行操作后需调用) void update_indexing(); // 更新索引(对网格的元素进行操作后需调用)
void sort_groups(); // 对单元体组进行梳理(对网格的元素进行操作后需调用) void sort_groups(); // 对单元体组进行梳理(对网格的元素进行操作后需调用)
element_type_enum match_type(const std::type_info &tinfo); // 根据type_info返回单元体类型
}; };
template <typename T>
void gctl::mesh_io::export_to(array<T> &elems, array<vertex3dc> &nodes)
{
const std::type_info &tinfo = typeid(T);
element_type_enum oe_type = match_type(tinfo);
size_t n = selected_nodes_.size();
size_t s = selected_elems_.size();
std::map<int, int> node_map;
nodes.resize(n);
for (size_t i = 0; i < n; i++)
{
node_map[selected_nodes_[i]->id] = i; // 原网格顶点索引到新网格顶点索引的映射
nodes[i].id = i;
nodes[i].x = selected_nodes_[i]->x;
nodes[i].y = selected_nodes_[i]->y;
nodes[i].z = selected_nodes_[i]->z;
}
int vnum = elem_size(oe_type);
elems.resize(s);
for (size_t i = 0; i < s; i++)
{
if (selected_elems_[i]->type != oe_type)
throw gctl::invalid_argument("[gctl::mesh_io::export_to] The selected element is not " + elem_name(oe_type) + ".");
elems[i].id = i;
for (size_t v = 0; v < vnum; v++)
elems[i].vert[v] = nodes.get(node_map[selected_elems_[i]->vert_ptrs[v]->id]);
}
node_map.clear();
return;
}
template <typename T>
void gctl::mesh_io::export_to(array<T> &elems, array<vertex2dc> &nodes, int x_id, int y_id)
{
const std::type_info &tinfo = typeid(T);
element_type_enum oe_type = match_type(tinfo);
size_t n = selected_nodes_.size();
size_t s = selected_elems_.size();
std::map<int, int> node_map;
nodes.resize(n);
double xyz_ref[3];
for (size_t i = 0; i < n; i++)
{
node_map[selected_nodes_[i]->id] = i; // 原网格顶点索引到新网格顶点索引的映射
nodes[i].id = i;
xyz_ref[0] = selected_nodes_[i]->x;
xyz_ref[1] = selected_nodes_[i]->y;
xyz_ref[2] = selected_nodes_[i]->z;
nodes[i].x = xyz_ref[x_id];
nodes[i].y = xyz_ref[y_id];
}
int vnum = elem_size(oe_type);
elems.resize(s);
for (size_t i = 0; i < s; i++)
{
if (selected_elems_[i]->type != oe_type)
throw gctl::invalid_argument("[gctl::mesh_io::export_to] The selected element is not " + elem_name(oe_type) + ".");
elems[i].id = i;
for (size_t v = 0; v < vnum; v++)
elems[i].vert[v] = nodes.get(node_map[selected_elems_[i]->vert_ptrs[v]->id]);
}
node_map.clear();
return;
}
}; };
#endif // _GCTL_MESH_IO_H #endif // _GCTL_MESH_IO_H

11
tool/dsviewer/example.csv Normal file
View File

@ -0,0 +1,11 @@
Name,Age,Gender,Occupation,City,Phone Number,Email,Annual Income,Company,Position
John Doe,28,Male,Engineer,New York,123-456-7890,john.doe@example.com,$75000,ABC Tech,Senior Software Engineer
Jane Smith,34,Female,Teacher,Los Angeles,234-567-8901,jane.smith@example.com,$55000,XYZ School,Math Teacher
Michael Brown,22,Male,Student,Toronto,345-678-9012,michael.brown@example.com,$18000,University of Toronto,Undergraduate Student
Emily Davis,45,Female,Doctor,Chicago,456-789-0123,emily.davis@example.com,$180000,Chicago Medical Center,Cardiologist
David Wilson,30,Male,Designer,San Francisco,567-890-1234,david.wilson@example.com,$90000,DesignWorks,Senior Graphic Designer
Sarah Johnson,29,Female,Marketing Specialist,Seattle,678-901-2345,sarah.johnson@example.com,$85000,MarketingPro,Marketing Manager
Robert Lee,36,Male,Software Developer,Boston,789-012-3456,robert.lee@example.com,$120000,SoftDev Solutions,Lead Developer
Laura Miller,25,Female,Graphic Designer,Denver,890-123-4567,laura.miller@example.com,$60000,Creative Arts,Junior Designer
William Clark,40,Male,Consultant,Atlanta,901-234-5678,william.clark@example.com,$110000,ConsultPro,Business Consultant
Olivia Turner,33,Female,Researcher,Phoenix,012-345-6789,olivia.turner@example.com,$70000,ResearchInnovate,Research Scientist
1 Name Age Gender Occupation City Phone Number Email Annual Income Company Position
2 John Doe 28 Male Engineer New York 123-456-7890 john.doe@example.com $75000 ABC Tech Senior Software Engineer
3 Jane Smith 34 Female Teacher Los Angeles 234-567-8901 jane.smith@example.com $55000 XYZ School Math Teacher
4 Michael Brown 22 Male Student Toronto 345-678-9012 michael.brown@example.com $18000 University of Toronto Undergraduate Student
5 Emily Davis 45 Female Doctor Chicago 456-789-0123 emily.davis@example.com $180000 Chicago Medical Center Cardiologist
6 David Wilson 30 Male Designer San Francisco 567-890-1234 david.wilson@example.com $90000 DesignWorks Senior Graphic Designer
7 Sarah Johnson 29 Female Marketing Specialist Seattle 678-901-2345 sarah.johnson@example.com $85000 MarketingPro Marketing Manager
8 Robert Lee 36 Male Software Developer Boston 789-012-3456 robert.lee@example.com $120000 SoftDev Solutions Lead Developer
9 Laura Miller 25 Female Graphic Designer Denver 890-123-4567 laura.miller@example.com $60000 Creative Arts Junior Designer
10 William Clark 40 Male Consultant Atlanta 901-234-5678 william.clark@example.com $110000 ConsultPro Business Consultant
11 Olivia Turner 33 Female Researcher Phoenix 012-345-6789 olivia.turner@example.com $70000 ResearchInnovate Research Scientist