{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Importing Data from Files & Importing Your Own Functions\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ":::{admonition} Learning Objectives\n", "* Update your Anaconda software: see [Update Anaconda](3114-welcome-update)\n", "* Be able to import an excel file and plot data using matplotlib\n", "* Be able to write your own functions, store them in a *.py file, and import them in a new Jupyter notebook using `import` \n", ":::" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ":::{important}\n", "When working through a lesson / reading this textbook, you should have a blank Jupyter Notebook open to try the codes written here. It is also important that you try changing parameters to see what the code is doing. Don't treat the example codes I use as a black box. You are expected to know what every line is doing. \n", ":::" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Import data from Excel" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "tags": [ "remove-input", "remove-output" ] }, "outputs": [], "source": [ "from jupyterquiz import display_quiz\n", "\n", "# tags: remove-input\n", "# menu: View/Cell toolbar/tags or in Jupyter lab use gear icon on top right\n", "# this will remove the code below when building Jupyter-book \n", "# could also use remove-cell but this code has no output\n", "\n", "example=[{\n", " \"question\": '''Create a new Jupyter notebook and import the data from Al7075_out.xlsx (The link\n", " above will allow you to download the file). \n", " Plot stress vs strain (label your axes and use blue triangles for your data points). \n", " Were you successful?''',\n", " \"type\": \"multiple_choice\",\n", " \"answers\": [\n", " {\n", " \"code\": \"Yes\",\n", " \"correct\": True,\n", " \"feedback\": \"Awesome!\"\n", " },\n", " {\n", " \"code\": \"No\",\n", " \"correct\": False,\n", " \"feedback\": '''Take a look at lesson 3 from 2114 located in Canvas. You won't need\n", " to do all of the manipulation we did in this lesson. You just need to plot the\n", " correct columns in Al7075_out.xlsx. I've already added a column for stress and \n", " one for strain.'''\n", " }\n", " ]\n", " }]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Data File: [Al7075_out.xlsx](https://drive.google.com/uc?export=download&id=14uBqZM8ekl1RoFgx3nwCJM7fe9N144RI) and lesson help from 2114: [Working with Data](2114:03:title)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "tags": [ "remove-input" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Click above to download the excel file needed for this exercise\n" ] }, { "data": { "text/html": [ "
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "var questionsQmcGnXAZDJDM=[{\"question\": \"Create a new Jupyter notebook and import the data from Al7075_out.xlsx (The link\\n above will allow you to download the file). \\n Plot stress vs strain (label your axes and use blue triangles for your data points). \\n Were you successful?\", \"type\": \"multiple_choice\", \"answers\": [{\"code\": \"Yes\", \"correct\": true, \"feedback\": \"Awesome!\"}, {\"code\": \"No\", \"correct\": false, \"feedback\": \"Take a look at lesson 3 from 2114 located in Canvas. You won't need\\n to do all of the manipulation we did in this lesson. You just need to plot the\\n correct columns in Al7075_out.xlsx. I've already added a column for stress and \\n one for strain.\"}]}];\n", " // Make a random ID\n", "function makeid(length) {\n", " var result = [];\n", " var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n", " var charactersLength = characters.length;\n", " for (var i = 0; i < length; i++) {\n", " result.push(characters.charAt(Math.floor(Math.random() * charactersLength)));\n", " }\n", " return result.join('');\n", "}\n", "\n", "// Choose a random subset of an array. Can also be used to shuffle the array\n", "function getRandomSubarray(arr, size) {\n", " var shuffled = arr.slice(0), i = arr.length, temp, index;\n", " while (i--) {\n", " index = Math.floor((i + 1) * Math.random());\n", " temp = shuffled[index];\n", " shuffled[index] = shuffled[i];\n", " shuffled[i] = temp;\n", " }\n", " return shuffled.slice(0, size);\n", "}\n", "\n", "function printResponses(responsesContainer) {\n", " var responses=JSON.parse(responsesContainer.dataset.responses);\n", " var stringResponses='IMPORTANT!To preserve this answer sequence for submission, when you have finalized your answers:
  1. Copy the text in this cell below \"Answer String\"
  2. Double click on the cell directly below the Answer String, labeled \"Replace Me\"
  3. Select the whole \"Replace Me\" text
  4. Paste in your answer string and press shift-Enter.
  5. Save the notebook using the save icon or File->Save Notebook menu item



  6. Answer String:
    ';\n", " console.log(responses);\n", " responses.forEach((response, index) => {\n", " if (response) {\n", " console.log(index + ': ' + response);\n", " stringResponses+= index + ': ' + response +\"
    \";\n", " }\n", " });\n", " responsesContainer.innerHTML=stringResponses;\n", "}\n", "function check_mc() {\n", " var id = this.id.split('-')[0];\n", " //var response = this.id.split('-')[1];\n", " //console.log(response);\n", " //console.log(\"In check_mc(), id=\"+id);\n", " //console.log(event.srcElement.id) \n", " //console.log(event.srcElement.dataset.correct) \n", " //console.log(event.srcElement.dataset.feedback)\n", "\n", " var label = event.srcElement;\n", " //console.log(label, label.nodeName);\n", " var depth = 0;\n", " while ((label.nodeName != \"LABEL\") && (depth < 20)) {\n", " label = label.parentElement;\n", " console.log(depth, label);\n", " depth++;\n", " }\n", "\n", "\n", "\n", " var answers = label.parentElement.children;\n", "\n", " //console.log(answers);\n", "\n", "\n", " // Split behavior based on multiple choice vs many choice:\n", " var fb = document.getElementById(\"fb\" + id);\n", "\n", "\n", "\n", "\n", " if (fb.dataset.numcorrect == 1) {\n", " // What follows is for the saved responses stuff\n", " var outerContainer = fb.parentElement.parentElement;\n", " var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n", " if (responsesContainer) {\n", " //console.log(responsesContainer);\n", " var response = label.firstChild.innerText;\n", " if (label.querySelector(\".QuizCode\")){\n", " response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n", " }\n", " console.log(response);\n", " //console.log(document.getElementById(\"quizWrap\"+id));\n", " var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n", " console.log(\"Question \" + qnum);\n", " //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n", " var responses=JSON.parse(responsesContainer.dataset.responses);\n", " console.log(responses);\n", " responses[qnum]= response;\n", " responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n", " printResponses(responsesContainer);\n", " }\n", " // End code to preserve responses\n", " \n", " for (var i = 0; i < answers.length; i++) {\n", " var child = answers[i];\n", " //console.log(child);\n", " child.className = \"MCButton\";\n", " }\n", "\n", "\n", "\n", " if (label.dataset.correct == \"true\") {\n", " // console.log(\"Correct action\");\n", " if (\"feedback\" in label.dataset) {\n", " fb.textContent = jaxify(label.dataset.feedback);\n", " } else {\n", " fb.textContent = \"Correct!\";\n", " }\n", " label.classList.add(\"correctButton\");\n", "\n", " fb.className = \"Feedback\";\n", " fb.classList.add(\"correct\");\n", "\n", " } else {\n", " if (\"feedback\" in label.dataset) {\n", " fb.textContent = jaxify(label.dataset.feedback);\n", " } else {\n", " fb.textContent = \"Incorrect -- try again.\";\n", " }\n", " //console.log(\"Error action\");\n", " label.classList.add(\"incorrectButton\");\n", " fb.className = \"Feedback\";\n", " fb.classList.add(\"incorrect\");\n", " }\n", " }\n", " else {\n", " var reset = false;\n", " var feedback;\n", " if (label.dataset.correct == \"true\") {\n", " if (\"feedback\" in label.dataset) {\n", " feedback = jaxify(label.dataset.feedback);\n", " } else {\n", " feedback = \"Correct!\";\n", " }\n", " if (label.dataset.answered <= 0) {\n", " if (fb.dataset.answeredcorrect < 0) {\n", " fb.dataset.answeredcorrect = 1;\n", " reset = true;\n", " } else {\n", " fb.dataset.answeredcorrect++;\n", " }\n", " if (reset) {\n", " for (var i = 0; i < answers.length; i++) {\n", " var child = answers[i];\n", " child.className = \"MCButton\";\n", " child.dataset.answered = 0;\n", " }\n", " }\n", " label.classList.add(\"correctButton\");\n", " label.dataset.answered = 1;\n", " fb.className = \"Feedback\";\n", " fb.classList.add(\"correct\");\n", "\n", " }\n", " } else {\n", " if (\"feedback\" in label.dataset) {\n", " feedback = jaxify(label.dataset.feedback);\n", " } else {\n", " feedback = \"Incorrect -- try again.\";\n", " }\n", " if (fb.dataset.answeredcorrect > 0) {\n", " fb.dataset.answeredcorrect = -1;\n", " reset = true;\n", " } else {\n", " fb.dataset.answeredcorrect--;\n", " }\n", "\n", " if (reset) {\n", " for (var i = 0; i < answers.length; i++) {\n", " var child = answers[i];\n", " child.className = \"MCButton\";\n", " child.dataset.answered = 0;\n", " }\n", " }\n", " label.classList.add(\"incorrectButton\");\n", " fb.className = \"Feedback\";\n", " fb.classList.add(\"incorrect\");\n", " }\n", " // What follows is for the saved responses stuff\n", " var outerContainer = fb.parentElement.parentElement;\n", " var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n", " if (responsesContainer) {\n", " //console.log(responsesContainer);\n", " var response = label.firstChild.innerText;\n", " if (label.querySelector(\".QuizCode\")){\n", " response+= label.querySelector(\".QuizCode\").firstChild.innerText;\n", " }\n", " console.log(response);\n", " //console.log(document.getElementById(\"quizWrap\"+id));\n", " var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n", " console.log(\"Question \" + qnum);\n", " //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n", " var responses=JSON.parse(responsesContainer.dataset.responses);\n", " if (label.dataset.correct == \"true\") {\n", " if (typeof(responses[qnum]) == \"object\"){\n", " if (!responses[qnum].includes(response))\n", " responses[qnum].push(response);\n", " } else{\n", " responses[qnum]= [ response ];\n", " }\n", " } else {\n", " responses[qnum]= response;\n", " }\n", " console.log(responses);\n", " responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n", " printResponses(responsesContainer);\n", " }\n", " // End save responses stuff\n", "\n", "\n", "\n", " var numcorrect = fb.dataset.numcorrect;\n", " var answeredcorrect = fb.dataset.answeredcorrect;\n", " if (answeredcorrect >= 0) {\n", " fb.textContent = feedback + \" [\" + answeredcorrect + \"/\" + numcorrect + \"]\";\n", " } else {\n", " fb.textContent = feedback + \" [\" + 0 + \"/\" + numcorrect + \"]\";\n", " }\n", "\n", "\n", " }\n", "\n", " if (typeof MathJax != 'undefined') {\n", " var version = MathJax.version;\n", " console.log('MathJax version', version);\n", " if (version[0] == \"2\") {\n", " MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n", " } else if (version[0] == \"3\") {\n", " MathJax.typeset([fb]);\n", " }\n", " } else {\n", " console.log('MathJax not detected');\n", " }\n", "\n", "}\n", "\n", "function make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id) {\n", " var shuffled;\n", " if (shuffle_answers == \"True\") {\n", " //console.log(shuffle_answers+\" read as true\");\n", " shuffled = getRandomSubarray(qa.answers, qa.answers.length);\n", " } else {\n", " //console.log(shuffle_answers+\" read as false\");\n", " shuffled = qa.answers;\n", " }\n", "\n", "\n", " var num_correct = 0;\n", "\n", "\n", "\n", " shuffled.forEach((item, index, ans_array) => {\n", " //console.log(answer);\n", "\n", " // Make input element\n", " var inp = document.createElement(\"input\");\n", " inp.type = \"radio\";\n", " inp.id = \"quizo\" + id + index;\n", " inp.style = \"display:none;\";\n", " aDiv.append(inp);\n", "\n", " //Make label for input element\n", " var lab = document.createElement(\"label\");\n", " lab.className = \"MCButton\";\n", " lab.id = id + '-' + index;\n", " lab.onclick = check_mc;\n", " var aSpan = document.createElement('span');\n", " aSpan.classsName = \"\";\n", " //qDiv.id=\"quizQn\"+id+index;\n", " if (\"answer\" in item) {\n", " aSpan.innerHTML = jaxify(item.answer);\n", " //aSpan.innerHTML=item.answer;\n", " }\n", " lab.append(aSpan);\n", "\n", " // Create div for code inside question\n", " var codeSpan;\n", " if (\"code\" in item) {\n", " codeSpan = document.createElement('span');\n", " codeSpan.id = \"code\" + id + index;\n", " codeSpan.className = \"QuizCode\";\n", " var codePre = document.createElement('pre');\n", " codeSpan.append(codePre);\n", " var codeCode = document.createElement('code');\n", " codePre.append(codeCode);\n", " codeCode.innerHTML = item.code;\n", " lab.append(codeSpan);\n", " //console.log(codeSpan);\n", " }\n", "\n", " //lab.textContent=item.answer;\n", "\n", " // Set the data attributes for the answer\n", " lab.setAttribute('data-correct', item.correct);\n", " if (item.correct) {\n", " num_correct++;\n", " }\n", " if (\"feedback\" in item) {\n", " lab.setAttribute('data-feedback', item.feedback);\n", " }\n", " lab.setAttribute('data-answered', 0);\n", "\n", " aDiv.append(lab);\n", "\n", " });\n", "\n", " if (num_correct > 1) {\n", " outerqDiv.className = \"ManyChoiceQn\";\n", " } else {\n", " outerqDiv.className = \"MultipleChoiceQn\";\n", " }\n", "\n", " return num_correct;\n", "\n", "}\n", "function check_numeric(ths, event) {\n", "\n", " if (event.keyCode === 13) {\n", " ths.blur();\n", "\n", " var id = ths.id.split('-')[0];\n", "\n", " var submission = ths.value;\n", " if (submission.indexOf('/') != -1) {\n", " var sub_parts = submission.split('/');\n", " //console.log(sub_parts);\n", " submission = sub_parts[0] / sub_parts[1];\n", " }\n", " //console.log(\"Reader entered\", submission);\n", "\n", " if (\"precision\" in ths.dataset) {\n", " var precision = ths.dataset.precision;\n", " // console.log(\"1:\", submission)\n", " submission = Math.round((1 * submission + Number.EPSILON) * 10 ** precision) / 10 ** precision;\n", " // console.log(\"Rounded to \", submission, \" precision=\", precision );\n", " }\n", "\n", "\n", " //console.log(\"In check_numeric(), id=\"+id);\n", " //console.log(event.srcElement.id) \n", " //console.log(event.srcElement.dataset.feedback)\n", "\n", " var fb = document.getElementById(\"fb\" + id);\n", " fb.style.display = \"none\";\n", " fb.textContent = \"Incorrect -- try again.\";\n", "\n", " var answers = JSON.parse(ths.dataset.answers);\n", " //console.log(answers);\n", "\n", " var defaultFB = \"\";\n", " var correct;\n", " var done = false;\n", " answers.every(answer => {\n", " //console.log(answer.type);\n", "\n", " correct = false;\n", " // if (answer.type==\"value\"){\n", " if ('value' in answer) {\n", " if (submission == answer.value) {\n", " fb.textContent = jaxify(answer.feedback);\n", " correct = answer.correct;\n", " //console.log(answer.correct);\n", " done = true;\n", " }\n", " // } else if (answer.type==\"range\") {\n", " } else if ('range' in answer) {\n", " //console.log(answer.range);\n", " if ((submission >= answer.range[0]) && (submission < answer.range[1])) {\n", " fb.textContent = jaxify(answer.feedback);\n", " correct = answer.correct;\n", " //console.log(answer.correct);\n", " done = true;\n", " }\n", " } else if (answer.type == \"default\") {\n", " defaultFB = answer.feedback;\n", " }\n", " if (done) {\n", " return false; // Break out of loop if this has been marked correct\n", " } else {\n", " return true; // Keep looking for case that includes this as a correct answer\n", " }\n", " });\n", "\n", " if ((!done) && (defaultFB != \"\")) {\n", " fb.innerHTML = jaxify(defaultFB);\n", " //console.log(\"Default feedback\", defaultFB);\n", " }\n", "\n", " fb.style.display = \"block\";\n", " if (correct) {\n", " ths.className = \"Input-text\";\n", " ths.classList.add(\"correctButton\");\n", " fb.className = \"Feedback\";\n", " fb.classList.add(\"correct\");\n", " } else {\n", " ths.className = \"Input-text\";\n", " ths.classList.add(\"incorrectButton\");\n", " fb.className = \"Feedback\";\n", " fb.classList.add(\"incorrect\");\n", " }\n", "\n", " // What follows is for the saved responses stuff\n", " var outerContainer = fb.parentElement.parentElement;\n", " var responsesContainer = document.getElementById(\"responses\" + outerContainer.id);\n", " if (responsesContainer) {\n", " console.log(submission);\n", " var qnum = document.getElementById(\"quizWrap\"+id).dataset.qnum;\n", " //console.log(\"Question \" + qnum);\n", " //console.log(id, \", got numcorrect=\",fb.dataset.numcorrect);\n", " var responses=JSON.parse(responsesContainer.dataset.responses);\n", " console.log(responses);\n", " if (submission == ths.value){\n", " responses[qnum]= submission;\n", " } else {\n", " responses[qnum]= ths.value + \"(\" + submission +\")\";\n", " }\n", " responsesContainer.setAttribute('data-responses', JSON.stringify(responses));\n", " printResponses(responsesContainer);\n", " }\n", " // End code to preserve responses\n", "\n", " if (typeof MathJax != 'undefined') {\n", " var version = MathJax.version;\n", " console.log('MathJax version', version);\n", " if (version[0] == \"2\") {\n", " MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n", " } else if (version[0] == \"3\") {\n", " MathJax.typeset([fb]);\n", " }\n", " } else {\n", " console.log('MathJax not detected');\n", " }\n", " return false;\n", " }\n", "\n", "}\n", "\n", "function isValid(el, charC) {\n", " //console.log(\"Input char: \", charC);\n", " if (charC == 46) {\n", " if (el.value.indexOf('.') === -1) {\n", " return true;\n", " } else if (el.value.indexOf('/') != -1) {\n", " var parts = el.value.split('/');\n", " if (parts[1].indexOf('.') === -1) {\n", " return true;\n", " }\n", " }\n", " else {\n", " return false;\n", " }\n", " } else if (charC == 47) {\n", " if (el.value.indexOf('/') === -1) {\n", " if ((el.value != \"\") && (el.value != \".\")) {\n", " return true;\n", " } else {\n", " return false;\n", " }\n", " } else {\n", " return false;\n", " }\n", " } else if (charC == 45) {\n", " var edex = el.value.indexOf('e');\n", " if (edex == -1) {\n", " edex = el.value.indexOf('E');\n", " }\n", "\n", " if (el.value == \"\") {\n", " return true;\n", " } else if (edex == (el.value.length - 1)) { // If just after e or E\n", " return true;\n", " } else {\n", " return false;\n", " }\n", " } else if (charC == 101) { // \"e\"\n", " if ((el.value.indexOf('e') === -1) && (el.value.indexOf('E') === -1) && (el.value.indexOf('/') == -1)) {\n", " // Prev symbol must be digit or decimal point:\n", " if (el.value.slice(-1).search(/\\d/) >= 0) {\n", " return true;\n", " } else if (el.value.slice(-1).search(/\\./) >= 0) {\n", " return true;\n", " } else {\n", " return false;\n", " }\n", " } else {\n", " return false;\n", " }\n", " } else {\n", " if (charC > 31 && (charC < 48 || charC > 57))\n", " return false;\n", " }\n", " return true;\n", "}\n", "\n", "function numeric_keypress(evnt) {\n", " var charC = (evnt.which) ? evnt.which : evnt.keyCode;\n", "\n", " if (charC == 13) {\n", " check_numeric(this, evnt);\n", " } else {\n", " return isValid(this, charC);\n", " }\n", "}\n", "\n", "\n", "\n", "\n", "\n", "function make_numeric(qa, outerqDiv, qDiv, aDiv, id) {\n", "\n", "\n", "\n", " //console.log(answer);\n", "\n", "\n", " outerqDiv.className = \"NumericQn\";\n", " aDiv.style.display = 'block';\n", "\n", " var lab = document.createElement(\"label\");\n", " lab.className = \"InpLabel\";\n", " lab.textContent = \"Type numeric answer here:\";\n", " aDiv.append(lab);\n", "\n", " var inp = document.createElement(\"input\");\n", " inp.type = \"text\";\n", " //inp.id=\"input-\"+id;\n", " inp.id = id + \"-0\";\n", " inp.className = \"Input-text\";\n", " inp.setAttribute('data-answers', JSON.stringify(qa.answers));\n", " if (\"precision\" in qa) {\n", " inp.setAttribute('data-precision', qa.precision);\n", " }\n", " aDiv.append(inp);\n", " //console.log(inp);\n", "\n", " //inp.addEventListener(\"keypress\", check_numeric);\n", " //inp.addEventListener(\"keypress\", numeric_keypress);\n", " /*\n", " inp.addEventListener(\"keypress\", function(event) {\n", " return numeric_keypress(this, event);\n", " }\n", " );\n", " */\n", " //inp.onkeypress=\"return numeric_keypress(this, event)\";\n", " inp.onkeypress = numeric_keypress;\n", " inp.onpaste = event => false;\n", "\n", " inp.addEventListener(\"focus\", function (event) {\n", " this.value = \"\";\n", " return false;\n", " }\n", " );\n", "\n", "\n", "}\n", "function jaxify(string) {\n", " var mystring = string;\n", "\n", " var count = 0;\n", " var loc = mystring.search(/([^\\\\]|^)(\\$)/);\n", "\n", " var count2 = 0;\n", " var loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n", "\n", " //console.log(loc);\n", "\n", " while ((loc >= 0) || (loc2 >= 0)) {\n", "\n", " /* Have to replace all the double $$ first with current implementation */\n", " if (loc2 >= 0) {\n", " if (count2 % 2 == 0) {\n", " mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\[\");\n", " } else {\n", " mystring = mystring.replace(/([^\\\\]|^)(\\$\\$)/, \"$1\\\\]\");\n", " }\n", " count2++;\n", " } else {\n", " if (count % 2 == 0) {\n", " mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\(\");\n", " } else {\n", " mystring = mystring.replace(/([^\\\\]|^)(\\$)/, \"$1\\\\)\");\n", " }\n", " count++;\n", " }\n", " loc = mystring.search(/([^\\\\]|^)(\\$)/);\n", " loc2 = mystring.search(/([^\\\\]|^)(\\$\\$)/);\n", " //console.log(mystring,\", loc:\",loc,\", loc2:\",loc2);\n", " }\n", "\n", " //console.log(mystring);\n", " return mystring;\n", "}\n", "\n", "\n", "function show_questions(json, mydiv) {\n", " console.log('show_questions');\n", " //var mydiv=document.getElementById(myid);\n", " var shuffle_questions = mydiv.dataset.shufflequestions;\n", " var num_questions = mydiv.dataset.numquestions;\n", " var shuffle_answers = mydiv.dataset.shuffleanswers;\n", "\n", " if (num_questions > json.length) {\n", " num_questions = json.length;\n", " }\n", "\n", " var questions;\n", " if ((num_questions < json.length) || (shuffle_questions == \"True\")) {\n", " //console.log(num_questions+\",\"+json.length);\n", " questions = getRandomSubarray(json, num_questions);\n", " } else {\n", " questions = json;\n", " }\n", "\n", " //console.log(\"SQ: \"+shuffle_questions+\", NQ: \" + num_questions + \", SA: \", shuffle_answers);\n", "\n", " // Iterate over questions\n", " questions.forEach((qa, index, array) => {\n", " //console.log(qa.question); \n", "\n", " var id = makeid(8);\n", " //console.log(id);\n", "\n", "\n", " // Create Div to contain question and answers\n", " var iDiv = document.createElement('div');\n", " //iDiv.id = 'quizWrap' + id + index;\n", " iDiv.id = 'quizWrap' + id;\n", " iDiv.className = 'Quiz';\n", " iDiv.setAttribute('data-qnum', index);\n", " mydiv.appendChild(iDiv);\n", " // iDiv.innerHTML=qa.question;\n", "\n", " var outerqDiv = document.createElement('div');\n", " outerqDiv.id = \"OuterquizQn\" + id + index;\n", "\n", " iDiv.append(outerqDiv);\n", "\n", " // Create div to contain question part\n", " var qDiv = document.createElement('div');\n", " qDiv.id = \"quizQn\" + id + index;\n", " //qDiv.textContent=qa.question;\n", " qDiv.innerHTML = jaxify(qa.question);\n", "\n", " outerqDiv.append(qDiv);\n", "\n", " // Create div for code inside question\n", " var codeDiv;\n", " if (\"code\" in qa) {\n", " codeDiv = document.createElement('div');\n", " codeDiv.id = \"code\" + id + index;\n", " codeDiv.className = \"QuizCode\";\n", " var codePre = document.createElement('pre');\n", " codeDiv.append(codePre);\n", " var codeCode = document.createElement('code');\n", " codePre.append(codeCode);\n", " codeCode.innerHTML = qa.code;\n", " outerqDiv.append(codeDiv);\n", " //console.log(codeDiv);\n", " }\n", "\n", "\n", " // Create div to contain answer part\n", " var aDiv = document.createElement('div');\n", " aDiv.id = \"quizAns\" + id + index;\n", " aDiv.className = 'Answer';\n", " iDiv.append(aDiv);\n", "\n", " //console.log(qa.type);\n", "\n", " var num_correct;\n", " if (qa.type == \"multiple_choice\") {\n", " num_correct = make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id);\n", " } else if (qa.type == \"many_choice\") {\n", " num_correct = make_mc(qa, shuffle_answers, outerqDiv, qDiv, aDiv, id);\n", " } else if (qa.type == \"numeric\") {\n", " //console.log(\"numeric\");\n", " make_numeric(qa, outerqDiv, qDiv, aDiv, id);\n", " }\n", "\n", "\n", " //Make div for feedback\n", " var fb = document.createElement(\"div\");\n", " fb.id = \"fb\" + id;\n", " //fb.style=\"font-size: 20px;text-align:center;\";\n", " fb.className = \"Feedback\";\n", " fb.setAttribute(\"data-answeredcorrect\", 0);\n", " fb.setAttribute(\"data-numcorrect\", num_correct);\n", " iDiv.append(fb);\n", "\n", "\n", " });\n", " var preserveResponses = mydiv.dataset.preserveresponses;\n", " console.log(preserveResponses);\n", " console.log(preserveResponses == \"true\");\n", " if (preserveResponses == \"true\") {\n", " console.log(preserveResponses);\n", " // Create Div to contain record of answers\n", " var iDiv = document.createElement('div');\n", " iDiv.id = 'responses' + mydiv.id;\n", " iDiv.className = 'JCResponses';\n", " // Create a place to store responses as an empty array\n", " iDiv.setAttribute('data-responses', '[]');\n", "\n", " // Dummy Text\n", " iDiv.innerHTML=\"Select your answers and then follow the directions that will appear here.\"\n", " //iDiv.className = 'Quiz';\n", " mydiv.appendChild(iDiv);\n", " }\n", "//console.log(\"At end of show_questions\");\n", " if (typeof MathJax != 'undefined') {\n", " console.log(\"MathJax version\", MathJax.version);\n", " var version = MathJax.version;\n", " setTimeout(function(){\n", " var version = MathJax.version;\n", " console.log('After sleep, MathJax version', version);\n", " if (version[0] == \"2\") {\n", " MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n", " } else if (version[0] == \"3\") {\n", " MathJax.typeset([mydiv]);\n", " }\n", " }, 500);\n", "if (typeof version == 'undefined') {\n", " } else\n", " {\n", " if (version[0] == \"2\") {\n", " MathJax.Hub.Queue([\"Typeset\", MathJax.Hub]);\n", " } else if (version[0] == \"3\") {\n", " MathJax.typeset([mydiv]);\n", " } else {\n", " console.log(\"MathJax not found\");\n", " }\n", " }\n", " }\n", " return false;\n", "}\n", "\n", " {\n", " show_questions(questionsQmcGnXAZDJDM, QmcGnXAZDJDM);\n", " }\n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from IPython.display import display, Markdown\n", "print(\"Click above to download the excel file needed for this exercise\")\n", "display_quiz(example)" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "Were you able to successfully create and run a new jupyter notebook using your Cheat Sheet notebook from 2114? **If not, fix it now.** The cheat sheet won't do you any good later if you can't quickly get up to speed after only 3 months off. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you were, then awesome! \n", ":::{image} ../images/awesome.png\n", ":height: 100px\n", ":::" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "remove-cell" ] }, "source": [ "\"../images/awesome.png\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Writing, Saving, and Importing Your Own Functions " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The steps are as follows:\n", "1. Write your own function in a jupyter notebook to make sure it works \n", "2. Copy the function and paste into a text file using notepad \n", "3. Save the file with a \".py\" extension" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Write your own function and test it" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's say we want to find the volume of the unit cell in a given crystal structure and we are given the lattice parameters. If this was a cubic unit cell of edge length \"a\", then the volume would just be $a^3$. \n", "\n", "What if our unit cell was triclinic with parameters a, b, c, $\\alpha$, $\\beta$, $\\gamma$ ? This is where we need the metric tensor, g, defined as: \n", "\n", "$$ g=\\begin{bmatrix}a^{2} & ab \\cos{(\\gamma)}&ac \\cos (\\beta) \\\\ab \\cos(\\gamma) & b^2 & bc \\cos (\\alpha) \\\\ ac \\cos(\\beta) & bc \\cos(\\alpha)&c^2 \\end{bmatrix}$$\n", "\n", "The unit cell volume will then just be the square root of the determinate of g, $\\sqrt{\\det(g)}$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ":::{hint}\n", "When dealing with matrices, you will want to use the numpy.linalg package. You can read the documentation here: https://numpy.org/doc/stable/reference/routines.linalg.html\n", ":::" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# let's start by importing numpy. We will need this for the functions: array, cos, det, and rad2deg\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 1. -0.89614723 -1.34422085]\n", " [-0.89614723 4. -2.6884417 ]\n", " [-1.34422085 -2.6884417 9. ]]\n" ] } ], "source": [ "# We want to work out the form of our metric tensor using variables but the array needs numbers so we will \n", "# use a simple for loop to replace our variables with numbers. This looks like:\n", "\n", "for a,b,c,alpha,beta,gamma in [[1,2,3,90,90,90]]:\n", " \n", " print(\n", " np.array([\n", " [a*a, a*b*np.cos(gamma), a*c*np.cos(beta)],\n", " [b*a*np.cos(gamma), b*b, b*c*np.cos(alpha)], \n", " [c*a*np.cos(beta),c*b*np.cos(alpha),c*c]\n", " ])\n", " )\n", " \n", "# I tried to place a lot of spacing above so you could see how the brackets all align but we don't need to do this. \n", "# Below I'll remove most of the space. \n", "# Also notice our output isn't quite right. The cos(90) -- all off diagonal terms -- should be zero. What is wrong?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ":::{important}\n", "Remember that trig functions expect radians not degrees so let's use the `deg2rad()` function to convert. \n", ":::" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1.0000000e+00 1.2246468e-16 1.8369702e-16]\n", " [1.2246468e-16 4.0000000e+00 3.6739404e-16]\n", " [1.8369702e-16 3.6739404e-16 9.0000000e+00]]\n" ] } ], "source": [ "for a,b,c,alpha,beta,gamma in [[1,2,3,90,90,90]]:\n", " \n", " print(np.array([[a*a, a*b*np.cos(np.deg2rad(gamma)), a*c*np.cos(np.deg2rad(beta))], \n", " [b*a*np.cos(np.deg2rad(gamma)), b*b, b*c*np.cos(np.deg2rad(alpha))], \n", " [c*a*np.cos(np.deg2rad(beta)),c*b*np.cos(np.deg2rad(alpha)),c*c]]))\n", " \n", "# notice our off diagonal terms in the output below are 10^-16. This is zero within our floating point precision. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next we want to find the *sqrt of the determinant of the gmatrix*. This is the unit cell volume. The det() function is in the subpackage linalg of numpy, i.e. `np.linalg.det()`." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6.0\n" ] } ], "source": [ "for a,b,c,alpha,beta,gamma in [[1,2,3,90,90,90]]:\n", " \n", " gmatrix=np.array([[a*a, a*b*np.cos(np.deg2rad(gamma)), a*c*np.cos(np.deg2rad(beta))], \n", " [b*a*np.cos(np.deg2rad(gamma)), b*b, b*c*np.cos(np.deg2rad(alpha))], \n", " [c*a*np.cos(np.deg2rad(beta)),c*b*np.cos(np.deg2rad(alpha)),c*c]])\n", " \n", " print(np.sqrt(np.linalg.det(gmatrix)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The above cells were just to test our code for the gmatrix and the volume output. Now we just need to write this as a function and remove the \"for\" statement. We can also add some information about our function. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ":::{important} \n", "Don't forget to include all import statements needed for the function to work. Your function will be copied into its own file so it needs to be self contained.\n", ":::" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "\"\"\"\n", "Unit Cell Volume\n", "\n", "The following function calculates the volume of a unit cell of any crystal system \n", "given the lattice parameters a,b,c,alpha,beta,gamma\n", "\"\"\"\n", "import numpy as np\n", "\n", "def vuc(a,b,c,alpha,beta,gamma):\n", " gmatrix=np.array([[a*a, a*b*np.cos(np.deg2rad(gamma)), a*c*np.cos(np.deg2rad(beta))], \n", " [b*a*np.cos(np.deg2rad(gamma)), b*b, b*c*np.cos(np.deg2rad(alpha))], \n", " [c*a*np.cos(np.deg2rad(beta)),c*b*np.cos(np.deg2rad(alpha)),c*c]])\n", " return np.sqrt(np.linalg.det(gmatrix))" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "23.999999999999993" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "vuc(2,3,4,90,90,90)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could clean things up a little bit if we just import the functions we need from numpy. Then we won't need to include np. before each call." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "\"\"\"\n", "Unit Cell Volume\n", "\n", "The following function calculates the volume of a unit cell of any crystal system \n", "given the lattice parameters a,b,c,alpha,beta,gamma\n", "\"\"\"\n", "from numpy import cos, array, deg2rad, sqrt\n", "from numpy.linalg import det\n", "\n", "def vuc(a,b,c,alpha,beta,gamma):\n", " g= array([[a*a, a*b*cos(deg2rad(gamma)), a*c*cos(deg2rad(beta))], \n", " [b*a*cos(deg2rad(gamma)), b*b, b*c*cos(deg2rad(alpha))], \n", " [c*a*cos(deg2rad(beta)),c*b*cos(deg2rad(alpha)),c*c]])\n", " return sqrt(det(g))" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "23.999999999999993" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "vuc(2,3,4,90,90,90)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Copy your function to a text file and save with extension \".py\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ":::{Important} By default your file explorer doesn't show extensions. You'll need to set this option before you can change the extension manually. [Instructions for Windows](https://support.microsoft.com/en-us/windows/common-file-name-extensions-in-windows-da4a4430-8e76-89c5-59f7-1cdbbc75cb01) or [MAC](https://support.apple.com/guide/mac-help/show-or-hide-filename-extensions-on-mac-mchlp2304/mac#:~:text=In%20the%20Finder%20on%20your,%E2%80%9CShow%20all%20filename%20extensions.%E2%80%9D) \n", ":::" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we know the above function works, we can use a text editor (e.g. notepad) to paste it into a text file with the extension \".py\". I called my file: \"my_func.py\". Make sure you save the file in the same directory as the notebook in which you want to import it. You can also tell windows to always open *\\*.py* files with notepad to make it easier to edit later. \n", "\n", ":::{Note}\n", "Some text file editors will save your file as \".py.txt\". In this case, you will have to go back in with your file explorer and delete the .txt extension. \n", ":::" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is the contents of my text file named \"my_functions.py\": \n", ":::{image} ../images/py_file_contents.png\n", ":height: 550px\n", ":::" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "remove-cell" ] }, "source": [ "\"../images/py_file_contents.png\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Import your *.py file into a new Jupyter notebook and test your function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Open a new notebook and import your function file. In my case, the command would be `import my_functions`. After importing, go ahead and test your function. In my case I would run `my_functions.vuc(2,3,4,90,90,90)`. Add another function to the same text file and test it." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I could also import using `import my_functions as mf`. Then I can shorten the code to use vuc(). Now I would run `mf.vuc(2,3,4,90,90,90)`. " ] }, { "cell_type": "markdown", "metadata": { "tags": [ "remove-cell" ] }, "source": [ "\"example" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ":::{image} ../images/import_my_functions.png\n", ":height: 300px\n", ":::" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ":::{note}\n", "We have seen this syntax before. For example, we import the numpy package using `import numpy as np`, and then to use a function within numpy (such as arange()) we would use `np.arange()`. \n", ":::" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercises" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(3114:02:problem-1)= \n", "### Problem 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Open a new Jupyter notebook and import the data from the excel file [Al7075_out.xlsx](https://drive.google.com/uc?export=download&id=14uBqZM8ekl1RoFgx3nwCJM7fe9N144RI). Using matplotlib, plot stress (y) vs strain (x), label your axes, and use blue triangles for your data points. Your plot should look like: " ] }, { "cell_type": "markdown", "metadata": { "tags": [ "remove-cell" ] }, "source": [ "\"stress" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ":::{figure} ../images/hw1_stress_strain_plot.png\n", ":height: 350px\n", ":name: figure-example \n", "Stress-Strain response for Al7075 in tension.\n", ":::" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Problem 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Write a function that takes the variables: (applied_load, initial_area, initial_length, and length_under_load) and returns the elastic modulus in units of GPa. Your function should include a comment area that specifies the units that are expected for each variable. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Problem 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Copy the function from the previous problem into a text file and save with the extension \".py\". Open a new notebook, import your .py file, and calculate the elastic modulus for a sample under an applied load of 5600 N resulting in a sample elongation of $7.35 \\times 10^{-2}$ mm. The initial sample diameter and length are 10 mm and 100 mm respectively. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Problem 4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Write a function that uses the metric tensor to find the angle $\\theta$ in degrees between 3 atoms as shown for example below. Your function should allow you to enter the fractional coordinates of the three atoms, [x1,y1,z1], [x2,y2,z2], and [x3,y3,z3], and the lattice parameters, a,b,c,alpha,beta,gamma. You can use the numbers below to test your code. The Oxygen - $Si_1$ bond length will be 1.60674 $\\mathring{A}$ and the Oxygen - $Si_2$ bond length will be 1.61035 $\\mathring{A}$. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ":::{image} ../images/hw1_gmatrix_problem.png\n", ":height: 450px\n", ":align: center\n", ":::" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "remove-cell" ] }, "source": [ "\"gmatrix" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "The vector, $\\overline{v1}$, connecting the $Si_2$ and O atoms is given by $\\overline{v1}=Si_2 - O = \\left[x2,y2,z2\\right]-\\left[x1,y1,z1\\right]=\\left[x2-x1,y2-y1,z2-z1\\right]$\n", "\n", "The magnitude of this vector (i.e. the bond length) is given by $\\mid\\overline{v1}\\mid=\\sqrt{\\overline{v1}\\cdot \\overline{\\overline{g}} \\cdot\\overline{v1}}$ or in matrix format: \n", "\n", "$$\\mid\\overline{v1}\\mid=\\sqrt{\\begin{bmatrix}x2-x1 & y2-y1 & z2-z1 \\end{bmatrix}\\cdot\\begin{bmatrix}g11&g12&g13\\\\g21&g22&g23\\\\g31&g32&g33 \\end{bmatrix}\\cdot\\begin{bmatrix}x2-x1 \\\\ y2-y1 \\\\ z2-z1 \\end{bmatrix}}$$\n", "\n", "Now the cosine of the angle between the $Si_2$ - O - $Si_1$ atoms would be given by:\n", "\n", "$$\\cos{\\theta}=\\frac{\\overline{v1}\\cdot \\overline{\\overline{g}} \\cdot \\overline{v2}}{\\mid\\overline{v1}\\mid\\ \\mid\\overline{v2}\\mid}$$\n", "where $\\overline{v2}=Si_1 - O$\n", "\n", "When performing your dot products of $\\overline{v2}$, $\\overline{\\overline{g}}$, and $\\overline{v1}$, you can use either of the two formats below. You do not need to worry about making a column versus row vector. Just define your vector as [1,2,3] for example. \n", "\n", "```python \n", " np.dot(np.dot(A, B), C)\n", " np.linalg.multi_dot([A,B,C])\n", "```" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.9" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }