Coverage for readers/gmsh/ui_versions/user_input_v2.py: 19%

137 statements  

« prev     ^ index     » next       coverage.py v7.7.0, created at 2025-03-20 20:51 +0100

1import json 

2import logging 

3from collections import defaultdict 

4from copy import deepcopy 

5 

6import yaml 

7 

8import nastranio.cards as nio_cards 

9from nastranio.cards import PROP2ELTS 

10from nastranio.constants import BOUNDARY 

11 

12DEFAULT_USERINPUT_TPL = { 

13 "format": 2, 

14 "exec": {"SOL": "SESTATIC"}, 

15 "params": { 

16 "PRGPST": "YES", 

17 "POST": -1, 

18 "OGEOM": "NO", 

19 "AUTOSPC": "YES", 

20 "K6ROT": 100.0, 

21 "GRDPNT": 0, 

22 "SKIPMGG": "YES", 

23 "CHKGRDS": "NO", 

24 }, 

25 "cases": { 

26 "default": { 

27 "id": -1, 

28 "TITLE": "LCID1", 

29 "ECHO": "NONE", 

30 "DISPLACEMENT(PLOT)": "ALL", 

31 "$ SPC": 1, 

32 "$ LOAD": 1, 

33 } 

34 }, 

35 "attributes": { 

36 "materials": { 

37 1: {"card": "MAT1", "params": {"E": 7e6, "NU": 0.33, "RHO": 5000.5}}, 

38 2: {"card": "MAT1", "params": {"E": 8e6, "NU": 0.33, "RHO": 5000.5}}, 

39 }, 

40 "properties": { 

41 1: { 

42 "card": "PBAR", 

43 "params": { 

44 "MID": 1, 

45 "A": 50.0, 

46 "I1": 18.0, 

47 "I2": 18.0, 

48 "J": 0.1, 

49 "I12": 0.0, 

50 }, 

51 }, 

52 2: { 

53 "card": "PBUSH", 

54 "params": { 

55 "K": "K", 

56 "K1": 1e4, 

57 "K2": 1e4, 

58 "K3": 1e4, 

59 "K4": 1e4, 

60 "K5": 1e4, 

61 "K6": 1e4, 

62 }, 

63 }, 

64 3: { 

65 "card": "PSHELL", 

66 "params": {"MID1": 1, "T": 3.0, "MID2": 1, "MID3": 1}, 

67 }, 

68 4: { 

69 "card": "PCOMP", 

70 "params": { 

71 "NSM": 0.0, 

72 }, 

73 "layup": [ 

74 {"MID": 1, "SOUT": "YES", "T": 0.018, "THETA": 0.0}, 

75 {"MID": 2, "SOUT": "YES", "T": 0.339, "THETA": 0.0}, 

76 {"MID": 1, "SOUT": "YES", "T": 0.018, "THETA": 5.0}, 

77 ], 

78 }, 

79 }, 

80 "affectations": {}, 

81 "boundaries": {}, 

82 "loading": {}, 

83 }, 

84} 

85 

86DEFAULT_BOUNDARY_INPUT = {"card": "SPC1", "params": {"C": "123456"}} 

87DEFAULT_LOADING_INPUT = { 

88 "card": "FORCE", 

89 "params": { 

90 "CID": 0, 

91 "F": 1.0, 

92 "N1": 1.0, 

93 "N2": 0.0, 

94 "N3": 0.0, 

95 "as_sum": False, 

96 "exclusive": False, 

97 }, 

98} 

99 

100 

101def _from_v1_carddata(carddata): 

102 """ 

103 was [<cardname>, <params>] 

104 now {"card": <cardname>, "params": <params>} 

105 """ 

106 try: 

107 return {"card": carddata[0], "params": carddata[1]} 

108 except: 

109 logging.critical(f"cannot import {carddata} from V1") 

110 

111 

112def from_v1(parameters): 

113 parameters = deepcopy(parameters) 

114 parameters["format"] = 2 

115 for section_name in ("materials", "properties"): 

116 section = parameters["attributes"][section_name] 

117 for entry_id, carddata in section.items(): 

118 parameters["attributes"][section_name][entry_id] = _from_v1_carddata( 

119 carddata 

120 ) 

121 for section_name in ("boundaries", "loading"): 

122 section = parameters["attributes"][section_name] 

123 for sid, group2carddata in section.items(): 

124 for grpname, carddata in group2carddata.items(): 

125 parameters["attributes"][section_name][sid][ 

126 grpname 

127 ] = _from_v1_carddata(carddata) 

128 return parameters 

129 

130 

131class UserInput: 

132 def load_parameters(self, parameters): 

133 convmap = {1: from_v1} 

134 format = parameters["format"] 

135 if format != DEFAULT_USERINPUT_TPL["format"]: 

136 logging.warning(f"importing from deprecated {format=}") 

137 parameters = convmap[format](parameters) 

138 self.imported_from = format 

139 else: 

140 self.imported_from = None 

141 self.parameters = parameters 

142 self.attributes = parameters["attributes"] 

143 

144 def get_default_template(self): 

145 return deepcopy(DEFAULT_USERINPUT_TPL) 

146 

147 def get_default_boundary_input(self): 

148 return deepcopy(DEFAULT_BOUNDARY_INPUT) 

149 

150 def get_default_loading_input(self): 

151 return deepcopy(DEFAULT_LOADING_INPUT) 

152 

153 def to_json(self, filepath=None, indent=2, sort_keys=False, **kwargs): 

154 txt = json.dumps(self.parameters, indent=indent, sort_keys=sort_keys, **kwargs) 

155 if filepath: 

156 with open(filepath, "w") as fh: 

157 fh.write(txt) 

158 return filepath 

159 return txt 

160 

161 def to_yaml(self, filepath=None, indent=2, sort_keys=False, **kwargs): 

162 txt = yaml.dump(self.parameters, sort_keys=sort_keys, **kwargs) 

163 if filepath: 

164 with open(filepath, "w") as fh: 

165 fh.write(txt) 

166 return filepath 

167 return txt 

168 

169 def get_card_data(self, carddata, **kwargs): 

170 """merge user input with card definition""" 

171 cardname = carddata["card"] 

172 params = carddata["params"] 

173 card = getattr(nio_cards, cardname) 

174 params = params.copy() 

175 if "XID" in kwargs: 

176 kwargs[card.XID_FIELDNAME] = kwargs.pop("XID") 

177 params.update(kwargs) 

178 fields_info = card.fields_info() 

179 mandatory = set(fields_info["mandatory"]) 

180 optional = fields_info["optional"] 

181 all = mandatory | optional 

182 unknown = set(params) - all 

183 missing = mandatory - set(params) 

184 ok_params = {k: v for k, v in params.items() if k in all} 

185 ret = { 

186 "card": card, 

187 "params": ok_params, 

188 } 

189 if hasattr(card, "LOADING_TYPE") and card.LOADING_TYPE is not None: 

190 ret["over"] = [card.LOADING_TYPE] 

191 if card.type == BOUNDARY: 

192 ret["over"] = ["nodes"] 

193 if missing: 

194 ret["missing"] = missing 

195 # --------------------------------------------------------------------- 

196 # repeated fields 

197 repeated = fields_info.get("repeated", ()) 

198 # if cardname == "PCOMP": 

199 # breakpoint() 

200 if repeated: 

201 repeated_data_raw = carddata.get(card.REPEATED_DATA_NAME) 

202 if repeated_data_raw is None: 

203 # SPC1 has a REPEATED_DATA_NAME, but not available at this point 

204 ret["repeated"] = repeated 

205 else: 

206 # PCOMP has a REPEATED_DATA_NAME, and data are available 

207 repeated_data = defaultdict(list) 

208 for data in repeated_data_raw: 

209 for fieldname, value in data.items(): 

210 repeated_data[fieldname + "i"].append(value) 

211 ret["repeated"] = dict(repeated_data) 

212 if unknown: 

213 ret["unknown"] = {k: v for k, v in params.items() if k in unknown} 

214 # eg for PCOMP: 

215 # { 

216 # 'card': <class 'nastranio.cards.properties.PCOMP'>, 

217 # 'params': {'NSM': 0.0, 'PID': 4}, 

218 # 'repeated': {'MIDi': [1, 1, 1], 

219 # 'THETAi': [0.0, 90.0, 0.0], 

220 # 'Ti': [0.01, 0.05, 0.01]}, 

221 

222 return ret 

223 

224 def get_materials(self): 

225 ret = {} 

226 for mid, carddata in self.attributes["materials"].items(): 

227 ret[mid] = self.get_card_data(carddata, XID=mid) 

228 return ret 

229 

230 def get_properties(self): 

231 """ 

232 return a dictionnary with cards specifications: 

233 

234 (Pdb++) pp(ret) 

235 {1: {'card': <class 'nastranio.cards.properties.PBAR'>, 

236 'params': {'A': 1.0, 'I1': 12.0, 'I2': 13.0, 'MID': 1, 'PID': 1}}, 

237 2: {'card': <class 'nastranio.cards.properties.PSHELL'>, 

238 'params': {'MID1': 1, 'MID2': 1, 'MID3': 1, 'PID': 2, 'T': 0.08}}, 

239 3: {'card': <class 'nastranio.cards.properties.PSHELL'>, 

240 'params': {'MID1': 1, 'MID2': 1, 'MID3': 1, 'PID': 3, 'T': 0.03}}, 

241 4: {'card': <class 'nastranio.cards.properties.PCOMP'>, 

242 'params': {'NSM': 0.0, 'PID': 4}, 

243 'repeated': {'MIDi': [1, 1, 1], 

244 'THETAi': [0.0, 90.0, 0.0], 

245 'Ti': [0.01, 0.05, 0.01]}}} 

246 """ 

247 ret = {} 

248 for pid, carddata in self.attributes["properties"].items(): 

249 ret[pid] = self.get_card_data(carddata, XID=pid) 

250 return ret 

251 

252 def get_loading(self): 

253 ret = defaultdict(list) 

254 for sid, data in self.attributes["loading"].items(): 

255 for grpname, carddata in data.items(): 

256 _data = self.get_card_data(carddata, XID=sid) 

257 if grpname != "*": 

258 _data["over"].append(grpname) 

259 ret[sid].append(_data) 

260 return dict(ret) 

261 

262 def get_boundaries(self): 

263 ret = defaultdict(list) 

264 for sid, data in self.attributes["boundaries"].items(): 

265 for grpname, carddata in data.items(): 

266 _data = self.get_card_data(carddata, XID=sid) 

267 if grpname != "*": 

268 _data["over"].append(grpname) 

269 ret[sid].append(_data) 

270 return dict(ret) 

271 

272 def get_affectations(self): 

273 ret = {} 

274 props = self.get_properties() 

275 for grpname, params in self.attributes["affectations"].items(): 

276 prop_card = props[params["PID"]]["card"] 

277 cards_choice = PROP2ELTS[prop_card.__name__] 

278 card_by_type = {} 

279 for cardname in cards_choice: 

280 gmsh_elem_type = getattr(nio_cards, cardname).gmsh_eltype 

281 _data = self.get_card_data( 

282 carddata={"card": cardname, "params": params} 

283 ) 

284 _data["over"] = ["elements", grpname] 

285 card_by_type[gmsh_elem_type] = _data 

286 ret[grpname] = card_by_type 

287 return ret