PolyFEM
Loading...
Searching...
No Matches
JSONUtils.cpp
Go to the documentation of this file.
1#include "JSONUtils.hpp"
2
5
6#include <fstream>
7#include <filesystem>
8
9#include <jse/jse.h>
10
11#include <Eigen/Geometry>
12
13namespace polyfem
14{
15 namespace utils
16 {
18 {
19 if (!args.contains("common"))
20 return;
21
22 const std::string common_params_path = resolve_path(args["common"], args["root_path"]);
23
24 if (common_params_path.empty())
25 return;
26
27 std::ifstream file(common_params_path);
28 if (!file.is_open())
29 log_and_throw_error("Unable to open common params {} file", common_params_path);
30
31 json common_params;
32 file >> common_params;
33 file.close();
34
35 // Recursively apply common params
36 const bool has_root_path = common_params.contains("root_path");
37 if (has_root_path)
38 common_params["root_path"] = resolve_path(common_params["root_path"], common_params_path);
39 else
40 common_params["root_path"] = common_params_path;
41 apply_common_params(common_params);
42
43 // If there is a root path in the common params, it overrides the one in the current params.
44 // This is somewhat backwards as normally current params override common params, but this is
45 // an easy way to make sure that the relative paths in common are correct.
46 if (has_root_path)
47 args["root_path"] = common_params["root_path"];
48
49 json patch;
50 if (args.contains("patch"))
51 {
52 patch = args["patch"];
53 args.erase("patch");
54 }
55
56 common_params.merge_patch(args);
57 if (!patch.empty())
58 common_params = common_params.patch(patch);
59 args = common_params;
60
61 args.erase("common"); // Remove common params from the final json
62 }
63
64 void expand_bc_sidecars(json &args, const json &rules)
65 {
66 if (!args.contains("boundary_conditions"))
67 return;
68
69 json &bcs = args["boundary_conditions"];
70 if (!bcs.contains("dirichlet_boundary") || !bcs["dirichlet_boundary"].is_array())
71 return;
72
73 const json root_path = args.contains("root_path") ? args["root_path"] : json("");
74
75 json expanded = json::array();
76 for (const auto &entry : bcs["dirichlet_boundary"])
77 {
78 if (entry.is_string())
79 {
80 const std::string path = resolve_path(entry, root_path);
81 if (std::filesystem::path(path).extension() == ".json")
82 {
83 std::ifstream file(path);
84 if (!file.is_open())
85 log_and_throw_error("Unable to open dirichlet_boundary {} file", path);
86
87 json sidecar;
88 try
89 {
90 file >> sidecar;
91 }
92 catch (const std::exception &e)
93 {
94 log_and_throw_error("Invalid JSON in dirichlet_boundary file {}: {}", path, e.what());
95 }
96 file.close();
97
98 if (!sidecar.is_array())
99 log_and_throw_error("dirichlet_boundary file {} must contain an array", path);
100
101 jse::JSE jse;
102 for (const auto &e : sidecar)
103 {
104 json filled = e;
105 expanded.push_back(jse.inject_defaults(filled, rules));
106 }
107 continue;
108 }
109 }
110 expanded.push_back(entry);
111 }
112 bcs["dirichlet_boundary"] = expanded;
113 }
114
115 Eigen::Matrix3d to_rotation_matrix(const json &jr, std::string mode)
116 {
117 std::transform(mode.begin(), mode.end(), mode.begin(), ::tolower);
118
119 if (jr.is_array() && jr.empty())
120 {
121 return Eigen::Matrix3d::Identity(3, 3);
122 }
123
124 Eigen::VectorXd r;
125 if (jr.is_number())
126 {
127 r.setZero(3);
128 assert(mode.size() == 1); // must be either "x", "y", or "z"
129 int i = mode[0] - 'x';
130 assert(i >= 0 && i < 3);
131 r[i] = jr.get<double>();
132 }
133 else
134 {
135 assert(jr.is_array());
136 r = jr;
137 }
138
139 if (mode == "axis_angle")
140 {
141 assert(r.size() == 4);
142 double angle = deg2rad(r[0]); // NOTE: assumes input angle is in degrees
143 Eigen::Vector3d axis = r.tail<3>().normalized();
144 return Eigen::AngleAxisd(angle, axis).toRotationMatrix();
145 }
146
147 if (mode == "quaternion")
148 {
149 assert(r.size() == 4);
150 Eigen::Vector4d q = r.normalized();
151 return Eigen::Quaterniond(q).toRotationMatrix();
152 }
153
154 // The following expect the input is given in degrees
155 r = deg2rad(r);
156
157 if (mode == "rotation_vector")
158 {
159 assert(r.size() == 3);
160 double angle = r.norm();
161 if (angle != 0)
162 {
163 return Eigen::AngleAxisd(angle, r / angle).toRotationMatrix();
164 }
165 else
166 {
167 return Eigen::Matrix3d::Identity();
168 }
169 }
170
171 Eigen::Matrix3d R = Eigen::Matrix3d::Identity();
172
173 for (int i = 0; i < mode.size(); i++)
174 {
175 int j = mode[i] - 'x';
176 assert(j >= 0 && j < 3);
177 Eigen::Vector3d axis = Eigen::Vector3d::Zero();
178 axis[j] = 1;
179 R = Eigen::AngleAxisd(r[j], axis).toRotationMatrix() * R;
180 }
181
182 return R;
183 }
184
185 bool is_param_valid(const json &params, const std::string &key)
186 {
187 return !params.is_null() && params.contains(key) && !params[key].is_null();
188 }
189 } // namespace utils
190} // namespace polyfem
std::string resolve_path(const std::string &path, const std::string &input_file_path, const bool only_if_exists=false)
void expand_bc_sidecars(json &args, const json &rules)
Expand string entries in dirichlet_boundary that point to .json files.
Definition JSONUtils.cpp:64
Eigen::Matrix3d to_rotation_matrix(const json &jr, std::string mode)
T deg2rad(T deg)
Definition JSONUtils.hpp:21
bool is_param_valid(const json &params, const std::string &key)
Determine if a key exists and is non-null in a json object.
void apply_common_params(json &args)
Definition JSONUtils.cpp:17
nlohmann::json json
Definition Common.hpp:9
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73