Small gap between updates. Many things were done, such as:
- Developing Python code to create the input files.
- Developing Python code that could run all the jobs in a batched way
- Developing python code to read the ODBs and get key information
- Use that information in MATLAB to then get more data.
- Develop Python code to get the contour images for me (Why do something repetitive for 2 hours if I can just spend a mere 6 hours automating it)
- Actual analysis of results
I ended up creating 434 jobs, which took over ~10 hours to run

I’m simply going to embed some files here and some in the pdf I’ll submit, such as the matlab code.
PatchAnalysisPy
Link to original from odbAccess import * from abaqus import * import os # from loadinginfo import createLoadingData from typing import Any import numpy as np import datetime import traceback mdb: Mdb = openMdb("../patch.cae") model = mdb.models["Model-1"] # set loading def jobWithLoading(mdb, model, dx, dz, dr, with_patch): # suppressing/enabling def setFeatureStatus(parent, objects, enable): if enable: return parent.resumeFeatures(objects) else: return parent.suppressFeatures(objects) def setStatus(parent, objects, enable): for object in objects: if enable: parent[object].resume() else: parent[object].suppress() setFeatureStatus(model.rootAssembly, ("Adhesive-Layer-1", "Patch-1"), with_patch) setStatus(model.constraints, ("Adhesive-Patch-1", "Surface-Adhesive"), with_patch) setStatus(model.fieldOutputRequests, ("Adhesive", "Composite"), with_patch) model.ExpressionField("RotationY", "Z*sin(%f) + Y*(cos(%f)-1)" % (dr, dr)) model.ExpressionField( "RotationZ", "Z * (cos(%f)-1) + Y*sin(%f) + %f" % (dr, dr, dz) ) model.boundaryConditions["StretchR"].setValues(u1=dx, ur1=-dr) # must be negative rotation patchText = "" if with_patch else "less" name = ("Patch%s%+04.2fX%+04.2fZ%+05.2fR" % (patchText, dx, dz, dr * 180 / pi)).replace(".", "_") print(name) return mdb.Job(name, model), name def writeInputsInRange(mdb, model, dxs, dzs, drs, patch_statuses): jobs = {} dxmax = max(1e-6,max(dxs)) dzmax = max(1e-6,max(dzs)) drmax = max(1e-6,max(drs)) print(dxmax, dzmax, drmax) for with_patch in patch_statuses: for dx in dxs: for dz in dzs: for dr in drs: if dx != dxmax and dz != dzmax and dr != drmax: continue job, name = jobWithLoading(mdb, model, dx, dz, dr, with_patch) job.writeInput() job = mdb.JobFromInputFile(name, name + ".inp") jobs[name] = (job, dx, dz, dr, with_patch) print(jobs[name]) return jobs def fieldComponent(fieldOutputs, fieldOutput, region, getComponent): return np.array(np.nan) if not region else np.array([ getComponent(value) for value in fieldOutputs[fieldOutput].getSubset(region=region).values ]) def volumeMean(fieldOutputs, fieldOutput, region, getComponent): fo = fieldComponent(fieldOutputs, fieldOutput, region, getComponent) return np.mean(fo) # print("FO:" + str(fo.shape)) # vol = fieldComponent(fieldOutputs, "EVOL", region, lambda v: v.data) # print("VOL:" + str(vol.shape)) # return np.average(fo, weights=vol) def getStatsForFrame(frame, assembly, stepLoading): hasPatch = stepLoading[3] != 0 # means we don't have to worry about weird float issues frameLoading = stepLoading[0:3] * frame.frameValue fo = frame.fieldOutputs # instances instances = assembly.instances crack_proximity = instances["SURFACE-TOP"].elementSets["CRACK-ANALYSIS"] crack = instances["SURFACE-TOP"].elementSets["SURFACE-WEAK"] surfaceElem = instances["SURFACE-TOP"].elementSets["PATCH-NO-CRACK"] surfaceNode = instances["SURFACE-TOP"].nodeSets["PATCH-NO-CRACK"] adhesive = None if ~hasPatch else instances["ADHESIVE-LAYER-1"] patch = None if ~hasPatch else instances["PATCH-1"] stretch = assembly.nodeSets["STRETCH"] # surface strains n rotation sE11 = volumeMean(fo, "LE", surfaceElem, lambda v: v.data[0]) sE12 = volumeMean(fo, "LE", surfaceElem, lambda v: v.data[3]) sUR = np.mean(fieldComponent(fo, "UR", surfaceNode, lambda v: v.data[0])) patchStressMax = np.max(fieldComponent(fo, "S", patch, lambda v: v.mises)) surfaceStressMax = np.max(fieldComponent(fo, "S", surfaceElem, lambda v: v.mises)) crackStressMax = np.max(fieldComponent(fo, "S", crack_proximity, lambda v: v.mises)) crackStrainE11 = np.max(fieldComponent(fo, "LE", crack, lambda v: v.data[0])) crackStrainEMax = np.max(fieldComponent(fo, "LE", crack, lambda v: v.maxInPlanePrincipal)) crackStrainE22 = np.max(fieldComponent(fo, "LE", crack, lambda v: v.data[1])) crackStrainE12 = np.max(fieldComponent(fo, "LE", crack, lambda v: v.data[3])) adhesiveDegradationMax = np.max(fieldComponent(fo, "SDEG", adhesive, lambda v: v.data)) adhesiveDegradationAvg= np.mean(fieldComponent(fo, "SDEG", adhesive, lambda v: v.data)) sRFX = fieldComponent(fo, "RF", stretch, lambda v: v.data[0]) sRFY = fieldComponent(fo, "RF", stretch, lambda v: v.data[1]) sRFZ = fieldComponent(fo, "RF", stretch, lambda v: v.data[2]) sPY = fieldComponent(fo, "COORD", stretch, lambda v: v.data[1]) + fieldComponent(fo, "U", stretch, lambda v: v.data[1]) sPZ = fieldComponent(fo, "COORD", stretch, lambda v: v.data[2]) + fieldComponent(fo, "U", stretch, lambda v: v.data[2]) stretchRMX = sPZ * sRFY - sPY * sRFZ # hashin it hashinFC = np.max(fieldComponent(fo, "HSNFCCRT", patch, lambda v: v.data)) hashinFT = np.max(fieldComponent(fo, "HSNFTCRT", patch, lambda v: v.data)) hashinMC = np.max(fieldComponent(fo, "HSNMCCRT", patch, lambda v: v.data)) hashinMT = np.max(fieldComponent(fo, "HSNMTCRT", patch, lambda v: v.data)) hashinModes = [hashinFC, hashinFT, hashinMC, hashinMT] hashinMax = max(hashinModes) return { "t" : frame.frameValue, "hasPatch": hasPatch, "dx" : frameLoading[0], "dz" : frameLoading[1], "dr" : frameLoading[2], "E11": sE11, "E12": sE12, "UR1": sUR, "RFX": np.sum(sRFX), "RFY": np.sum(sRFY), "RFZ": np.sum(sRFZ), "RMX": np.sum(stretchRMX), "AdhDMax": adhesiveDegradationMax, "AdhDAvg": adhesiveDegradationAvg, "PtchSMax": patchStressMax, "SurfSMax": surfaceStressMax, "CrckSMax": crackStressMax, "CrckE11": crackStrainE11, "CrckE12": crackStrainE12, "CrckE22": crackStrainE22, "CrckEMax": crackStrainEMax, "HshnMax": hashinMax, "HshnType": hashinModes.index(hashinMax), } def getStatsForStep(step, assembly, stepLoading): return[getStatsForFrame(frame, assembly, stepLoading) for frame in step.frames] def dictToCsvString(data: list[dict[str,Any]]): return "\n".join([", ".join([f"{value}" for value in data[0].keys()])] + \ [", ".join([f"{value:+.8e}" for value in row.values()]) for row in data]) # jobs = writeInputsInRange( # mdb, model, np.linspace(0, 0.7, 8), np.linspace(0, 1, 8), np.linspace(0, pi/18, 8), [True, False] # ) jobs = writeInputsInRange( mdb, model, np.linspace(0, 0.5, 9), np.linspace(0, 0.9, 9), np.linspace(0, pi/18, 9), [True, False] ) # uniaxial cases: if(False): jobs = {} jobs.update(writeInputsInRange(mdb, model, [1], [0], [0], [False, True])) jobs.update(writeInputsInRange(mdb, model, [0], [1], [0], [False, True])) jobs.update(writeInputsInRange(mdb, model, [0], [0], [pi/12], [False, True])) def writeOdbStats(jobName, job, batchFilename): count_errored = 0 odb = None try: odb = openOdb("./" + jobName + ".odb", readOnly=True) step = odb.steps["Step-1"] loading = np.array(jobs[jobName][1:5]) csvstring = dictToCsvString(getStatsForStep(step, odb.rootAssembly, loading)) except Exception: count_errored+=1 extxt = traceback.format_exc() print(extxt) if odb: odb.close() return 1 raise with open("../csvs2/" + jobName + "-data.csv", "w") as file: file.write(csvstring) if batchFilename: with open("../csvs2/" + batchFilename + ".csv", "a") as file: file.write("\n"+csvstring) with open("../csvs2/collated-data.csv", "a") as file: file.write("\n"+csvstring) if odb: odb.close() return 0 n = 6 count_total = len(jobs) count_completed = 0 count_errored = 0 pending_jobs = list(jobs.keys()) stats = {} batchFilename = "Data-" + datetime.datetime.now().strftime("%Y-%m-%dT%H_%M_%S") while pending_jobs: jobsToComplete: list[tuple[str,Any]] = [] jobsAdded = 0 while pending_jobs and jobsAdded < 6: jobName = pending_jobs.pop() if os.path.exists("../csvs2/" + jobName + "-data.csv"): print(f"Skipped submitting {jobName}. data exists") continue # don't redo existing jobs # todo rewrite job = jobs[jobName][0] print(f"submitting {jobName}") job.submit() jobsToComplete.append((jobName,job)) jobsAdded+=1 for jobName, job in jobsToComplete: job.waitForCompletion() count_completed+=1 odb = None count_errored += writeOdbStats(jobName, jobs[jobName], batchFilename) print(f"Jobs completed {count_completed}/{count_total} ({count_errored} errors)")
AbaqusMacrosPy
Link to original # -*- coding: mbcs -*- # Do not delete the following import lines from abaqus import * from abaqusConstants import * import __main__ FRAME_FOR_LOADING = { "+0_50X+0_00Z+0_00R": 8, "+0_00X+0_90Z+0_00R": 9, "+0_00X+0_00Z+10_00R": 17, "+0_12X+0_90Z+2_50R": 8, "+0_50X+0_00Z+10_00R": 9 } def createNextViewport(): maxNum = max(map(lambda vp: int(vp.split(" ")[1]), session.viewports.keys())) session.Viewport(f"Viewport: {maxNum+1}") def manageViewportCount(desiredCount): print(f"Want {desiredCount} viewports and have {len(session.viewports)}") sortedKeys = sorted(session.viewports.keys(), key = lambda vp: int(vp.split(" ")[1])) if desiredCount < len(session.viewports) and False: # actually don't ever delete viewports session.viewports[sortedKeys[0]].makeCurrent() print("Removing excess viewports") for i, key in enumerate(sortedKeys): if i >= desiredCount: del session.viewports[key] elif desiredCount > len(session.viewports): print("Adding needed viewports") for i in range(len(session.viewports), desiredCount): createNextViewport() # re-sort sortedKeys = sorted(session.viewports.keys(), key = lambda vp: int(vp.split(" ")[1])) vps = {i:session.viewports[i] for i in sortedKeys} return vps def ShowFieldOutput(viewportSettings): vps = manageViewportCount(len(viewportSettings)) usedViewports = []; for settings, viewport in zip(viewportSettings,vps.values()): viewport.view.setProjection(projection=PARALLEL) viewport.odbDisplay.contourOptions.setValues(maxAutoCompute=ON, minAutoCompute=ON) viewport.makeCurrent() viewport.odbDisplay.contourOptions.setValues( colorByMatchingPlies=ON if settings[3] else OFF) if settings[1]: viewport.odbDisplay.setPrimaryVariable( variableLabel=settings[0], outputPosition=INTEGRATION_POINT, refinement=settings[1] ) else: viewport.odbDisplay.setPrimaryVariable( variableLabel=settings[0], outputPosition=INTEGRATION_POINT ) viewport.odbDisplay.basicOptions.setValues( sectionResults=USE_ENVELOPE, envelopeCriteria=settings[2] ) viewport.odbDisplay.display.setValues(plotState=(CONTOURS_ON_UNDEF,)) usedViewports.append(viewport) # get mins and maxes legendGroups = set([settings[4] for settings in viewportSettings]) for lg in legendGroups: if lg is None: continue connectedVPsIx = [i for i,settings in enumerate(viewportSettings) if settings[4] == lg] connectedVPs = [usedViewports[i] for i in connectedVPsIx] lgMin = 1e9 lgMax = -1e9 for vp in connectedVPs: lgMax = max(lgMax, vp.odbDisplay.contourOptions.autoMaxValue) lgMin = min(lgMax, vp.odbDisplay.contourOptions.autoMinValue) # now that we have min/max set to that for vp in connectedVPs: vp.odbDisplay.contourOptions.setValues( maxAutoCompute=OFF, maxValue=lgMax, minAutoCompute=OFF, minValue=lgMin) return usedViewports def FOStress(): viewportSettings = [ ("S", (INVARIANT, "Mises"), MAX_VALUE, False, 0), ("S", (INVARIANT, "Mises"), MIN_VALUE, False, 0), ("S", (COMPONENT, "S11"), MAX_VALUE, False, 1), ("S", (COMPONENT, "S11"), MIN_VALUE, False, 1), ("S", (COMPONENT, "S22"), MAX_VALUE, False, 2), ("S", (COMPONENT, "S22"), MIN_VALUE, False, 2), ("S", (COMPONENT, "S12"), MAX_VALUE, False, 3), ("S", (COMPONENT, "S12"), MIN_VALUE, False, 3) ] return ShowFieldOutput(viewportSettings) def FOHashin(): viewportSettings = [ ("HSNFCCRT", None, MAX_VALUE, False, None), ("HSNFCCRT", None, MIN_VALUE, True, None), ("HSNFTCRT", None, MAX_VALUE, False, None), ("HSNFTCRT", None, MIN_VALUE, True, None), ("HSNMCCRT", None, MAX_VALUE, False, None), ("HSNMCCRT", None, MIN_VALUE, True, None), ("HSNMTCRT", None, MAX_VALUE, False, None), ("HSNMTCRT", None, MIN_VALUE, True, None) ] return ShowFieldOutput(viewportSettings) def FOAdhesive(): viewportSettings = [ ("SDEG", None, MAX_VALUE, False, None), ("S", (INVARIANT, "Mises"), MAX_VALUE, False, None) ] return ShowFieldOutput(viewportSettings) def ViewSettings(): import connectorBehavior viewport = session.viewports.values()[0] viewport.viewportAnnotationOptions.setValues(title=OFF, state=OFF, compass=OFF) viewport.viewportAnnotationOptions.setValues( triadPosition=(95, 90)) viewport.viewportAnnotationOptions.setValues( legendFont='-*-verdana-medium-r-normal-*-*-70-*-*-p-*-*-*') def StressSurface(): session.viewports[session.currentViewportName].odbDisplay.contourOptions.setValues( maxAutoCompute=OFF, maxValue=345, minAutoCompute=OFF, minValue=-345) def StressPatch(): session.viewports[session.currentViewportName].odbDisplay.contourOptions.setValues( maxAutoCompute=OFF, maxValue=400, minAutoCompute=OFF, minValue=-400) def ContourColors(): session.viewports[session.currentViewportName].odbDisplay.contourOptions.setValues( outsideLimitsAboveColor='#FF00F8', outsideLimitsBelowColor='#6C008A') def SaveToImage(imageInfo, viewports): size = (3840, 2010)# (2560, 1340) session.printOptions.setValues(vpDecorations=OFF, reduceColors=False) session.pngOptions.setValues(imageSize = size) odbName = session.viewports.values()[0].displayedObject.name.split("/")[-1].strip(".odb"); loading = odbName.strip("Patch").strip("less"); hasPatch = not "less" in odbName fileName = loading + "-" + imageInfo if imageInfo == "SurfaceStress": fileName += "-Patch" if hasPatch else "-Patchless" filePatch = 'C:/Users/S3943498/OneDrive - RMIT University/Documents/Capstone personal/Patch/images/' + fileName session.printToFile( fileName=filePatch, format=PNG, canvasObjects=tuple(viewports)) print(f"Saving {imageInfo} to ./images/{fileName}") def ShowSurface(): import displayGroupOdbToolset as dgo leaf = dgo.LeafFromElementSets(elementSets=('SURFACE-TOP.REMESH', )) session.viewports[session.currentViewportName].odbDisplay.displayGroup.replace(leaf=leaf) session.viewports[session.currentViewportName].view.setValues(session.views['User-1']) def ShowPatch(): import displayGroupOdbToolset as dgo leaf = dgo.LeafFromPartInstance(partInstanceName=('PATCH-1', )) session.viewports[session.currentViewportName].odbDisplay.displayGroup.replace(leaf=leaf) session.viewports[session.currentViewportName].view.setValues(session.views['User-3']) def ShowAdhesive(): import displayGroupOdbToolset as dgo leaf = dgo.LeafFromPartInstance(partInstanceName=('ADHESIVE-LAYER-1', )) session.viewports[session.currentViewportName].odbDisplay.displayGroup.replace(leaf=leaf) session.viewports[session.currentViewportName].view.setValues(session.views['User-2']) def APatchStress(): ShowPatch() SaveToImage("PatchStress", FOStress()) def APatchHashin(): ShowPatch() SaveToImage("PatchHashin", FOHashin()) def ASurfaceStress(): ShowSurface() SaveToImage("SurfaceStress", FOStress()) def AAdhesiveAll(): ShowAdhesive() SaveToImage("AdhesiveAll", FOAdhesive()) def AAAGetImagesForOdb(odb): odbName = odb.name.split("/")[-1].strip(".odb") loading = odbName.strip("Patch").strip("less") # get frame for this odb if loading in FRAME_FOR_LOADING: frameIx = FRAME_FOR_LOADING[loading] frame = odb.steps['Step-1'].frames[frameIx] else: frameIx = len(odb.steps['Step-1'].frames)-1 print(odbName + " : " + loading + ", Frame " + str(frameIx)) for viewport in session.viewports.values(): viewport.setValues(displayedObject = odb) viewport.odbDisplay.setFrame(step='Step-1', frame=frameIx) manageViewportCount(8) if "less" in odbName: # patchless, only do surface ASurfaceStress() else: ASurfaceStress() APatchHashin() APatchStress() # have to do stress before changing element set AAdhesiveAll() def AAAGetImagesForViewportOdb(): odb = session.viewports[session.currentViewportName].displayedObject AAAGetImagesForOdb(odb) def AAAAGetImagesForLoadedOdbs(): for odb in session.odbs.values(): AAAGetImagesForOdb(odb) def ZZImports(): import section import regionToolset import displayGroupMdbToolset as dgm import part import material import assembly import step import interaction import load import mesh import optimization import job import sketch import visualization import xyPlot import displayGroupOdbToolset as dgo import connectorBehavior pass