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

1 import json

2 import logging

3 from collections import defaultdict

4 from copy import deepcopy

5

6 import yaml

7

8 import nastranio.cards as nio_cards

9 from nastranio.cards import PROP2ELTS

10 from nastranio.constants import BOUNDARY

11

12 DEFAULT_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

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

87 DEFAULT_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

101 def _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

112 def 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

131 class 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