Last week I took a “mental day” off from my blog because I was feeling like many of you and I’m not saying that crying is an effective use of my personal reserves of water and electrolytes long term… just that sometimes it just feels really good! 😛

I was able to upload my simulation code that I mentioned in my last post to GitHub so it wasn’t like I got nothing accomplished!

Also, it’s nice to see it was cloned a few times leading me to hope that it is helping people, though we’ll discuss the simulation later in this post. 🙂

In unrelated but relevant news about some “notable playthroughs” that ended in “game overs” in the the last couple weeks.

There comes a time in life when even the gambling mavericks among us knows when to “fold em” and “cash out”, after all the dealin’ is done… sadly we lost both Kenny Rodgers and Bill Withers just ten days apart! 😦

And I know, I know, I know, I…,  I know, I know! Hey, I oughta this leave the young topic alone!

It’s just that, ain’t no sunshine when their gone, only darkness every day… and as the old adage goes “death comes in threes” so, I really started to worry… about Betty White! 😛

Well, that is before I remembered she’s probably immortal and logic willing, will almost certainly outlast us all!

Gallows humor aside, I know a lot of you are here just for the art and maybe a few laughs if I could ever manage to write something funny, so… with that in mind here’s today’s edition of Joy Finger Paints.

Joy Survives Post-Apocalyptia Image Set:

“But.. I’m just here for the code!”

Look…

I’ll totally admit that over the years I’ve maybe played a little too much Fallout and 7 Days to Die but I swear I don’t just play apocalyptic games, I just happen to enjoy them! 😛

And… as a stereotypical totally a self-absorbed millennial…

I egotistically had to make a set of post-apocalyptic posters of myself enjoying this spring 2020’s latest in high fashion!

In fact… here’s a few “geek sneak peeks” from my upcoming spring/summer lookbook.

The whole collection is made from tyvek and acetate for his or her pleasure!

Note the zombie resistant face shield for that extra level of protection when you can’t avoid adventuring through crowds of infected!

These thumbnails link to my Patreon where you can view and download them for free.

Each fabulous look depicts a variety of different scenarios that make up the “post-apocalyptic” genera.

You can tell they are different because the colors change! 😛

Pretty sure one of them could take place in the post apocalyptic  Rick and Morty episode Rickmancing the Stone.

Hmmm… now that I think about it though, the images could depict the various stages of my descent (not the game, though oddly in that case a virus is still involved) into the clutches of the zombie virus… Oh wow! I just realized that I am one of the Hazmat Zombies in 7 Days to Die… huh, looks like I made it between 5 to 8 days depending on if you include the blue pix! 😛

 

 

Now if you are wondering why I only have the thumbnails here instead of the full images, it’s basically because Patreon offers me unlimited storage (more or less)  whereas I have a finite storage amount on my blog and I feel the images have a certain charm close up, so  I didn’t resize them down from 1920 x 2300 px (~2.8 MB each).

Which is your favorite? Leave a comment and let me know.

Now to more consequential affairs!

An ⍺-𝚶, “Alpha” to “Omicron”… April Adage

My atrocious affectations aside, there are ardent appeals for appropriations of PPE because no matter how assiduous Alan Alda’s auroral attempts are to agilely assuage the afflicted and arrest the arduous apocalyptic antecedent (#AnnihilateTheArc), the aforesaid aftermath is antipodal to an aspired aftermost, although… assorted avowal assertions are advanced that absent the accoutrements, they will accomplish our ambition to attack, anull, abolish and abrogate the affliction!

As much, according to alleged accounts, all anterior aid attendants are apparently anticipating the assumptive acquisition of the ailment and await apprehensively the AbEnd of, albeit abide, the advancing abstinence of abatement appliance, articles and apparatus.

Alright, all appurtenances advised… I’m not saying that’s going to happen and it probably wont this time (most places, they did say 3M had to produce for domestic use first and besides, weren’t some of those claims of lack of PPE debunked? Not saying all were but… perhaps it’s not as bad as some claims have suggested?), but that is enough of a reason to treat this and all future pandemics seriously!

Dang, er… I mean, abhorrence! I should have aforesaid “accept and administer the all-over-anguish as acute admonishment against all approaching adversity!” 😛

Absolutely astonishing! I acknowledge and I am aware that aloof arrogant arguments are audacious abuses of my alienated admirers allotment of time in the annals of anthology who are altogether accustomed to an assured amount of assertive acumen on my account, as such… I am now going to abandon my “a” aligned address and antics in accommodation of alacrity… admitting as much that, arguably I could act abundantly to advance that aesthetic if my appetite allowed me the agency and abandon to be adrift.

Okay, ancillary absurdity aside… 😛

Are Makeshift Masks… Safe?

Whether you are struggling to escape Raccoon City and face countless hoards of infected or just need to get through the pandemic du jour you may find the need to rely on makeshift personal protective equipment like cloth masks to ensure your personal safety.

Also, everyone seems to forget about eye covering but that’s out of the scope of today’s discussion. 😛

And… provided I didn’t delete any words prior to this point (added a little padding here and there) I should be just over one thousand words meaning that the censorship bot has probably stopped eavesdropping and left the room and we can now speak freely.

How can we be sure (more or less) that when we’re told to use fabric masks that it isn’t just a way for “the authorities” to control public fear?

Quite honestly, I’ve seen some terribly half baked recommendations such as “just cover your mouth and nose with a bandana or t-shirt”, which if only a single layer, I can basically prove offers almost no protection to you!

But like me you might also be asking… what about my Uptown Girls “sheets of Egyptian cotton”, would they be better than a t-shirt?

What if we used multiple layers, would that be better (it is) and if so, could we figure out how many layers would (more or less) provide “sufficient” protection?

Well, I could rely on my thousands of adoring Patrons to fund real world materials safety research but I feel like their hard earned cash is best spent supporting my work in other ways!

Besides… the obvious simple solution is to build a simulation! 😛

#FullDisclaimer

I do not intend to misrepresent the truth or present any intentionally incorrect information, though I am not a medical professional and the results of the viral particle distribution in the droplets, as well as the size of the droplets are probabilistic meaning I try to approximate a real sneeze as close as possible but not actually recreate any specific sneeze.

Additionally, the locations of the droplets are generated using a “Normal distribution“, specifically I used the Marsaglia polar method for generating a two dimensional Gaussian because it models a sneeze or cough stippling pattern perfectly (in my opinion).

Example Sneeze

Example Sneeze using Marsaglia polar sampling algorithm.
Example Sneeze

In any case, even if my simulations output should be taken with a grain of salt, I have made every effort to generate results consistent with reality.

Which brings us to talking about size.

Size

According to this wiki article on Coronavirus:

“The diameter of the virus particles is around 120 nm.” (0.12 microns)”

A comparison of how many Covid-19 viral particles will fit inside 1 micron cubed.

Covid-19 Size Example
Covid-19 Size Example

For reference and perspective, human hair is between 15 to 180 microns.

Size comparison of 1 micron to a 100 micron illustration of a human hair.
How big is 1 micron?

So, the image generated by my simulation uses several “units of measure” to create “boundaries” that the simulation work inside.

The “base unit” of measure used in this simulation is 1 micron and 1 pixel = 1 micron.

A micron is 1/1000000 meters which is about ~0.0000393701 inches (smaller than you can see).

It might be interesting to note that given the size of the average Covid-19 viral particle is ~0.12 microns we can figure out how many viral particles can fit inside 1 micron/pixel:

1 micron / ~0.12 = ~8.33

That means that approximately in one direction of a cubed micron about 8 virus particles could be stacked end to end.

We can then multiply that value by itself three times to get the approximate total number of viruses that could fit inside 1 pixel in my simulation:

~8.33 * ~8.33 * ~8.33 = ~578.009537 viral particles that can fit in each 1 pixel red dot.

The largest unit used is 1 inch, though if you have the hardware you can run simulations larger than 1 inch.

For example, my test simulations were run at 0.10% (10%) the size of 1 inch.

Meaning that the image it generated was 2540 x 2540 px or 6451600 microns square.

Which is tiny if compared the one inch simulation (25400 x 25400 px, 645160000 microns square) or even the 4 inch sim (101600 x 101600 px, 10322560000 microns square).

So, how big is 10% of 1 square inch?

A comparison of the simulation size vs. a U.S. penny
A comparison of the simulation size vs. a U.S. penny

According to a google search, a penny is ~19,053 microns in diameter.

Which brings us to the colors I used in the simulation…

Colors

I kept it simple:

  • Blue = Fabric/Thread (the Sheets of Egyptian cotton)
  • Green = Mucus without viral particles (Yucky! But not dangerous)
  • Red = Mucus with viral particles (Danger!)

How Does The Simulation Work?

With all of that out of the way, let me walk you through a simulation.

There are several “variables” that allow users to modify simulation size and the fabric, sneeze and viral characteristics.

Here’s a list of the user modifiable variables:

Simulation Characteristics
Variable Use Type
simulation_square_size_inches Define the size of the simulation as a percentage of 1 inch Float
Fabric Characteristics
Variable Use Type
fabric_simulation_method Used to change the method used to simulate the fabric Defined
fabric_mesh_size_microns_or_thread_count Used to enter the mesh size in microns wide or as a thread count per square inch Int
thread_thickness_method Used to change the thread thickness method (micron, denier, tex) Defined
thread_size_micron_denier_tex The size as micron, denier or tex for the selected thread sizing method Int/Float
fiber_density_grams_per_millileter The fiber density of the thread as grams per milliliter (only necessary if using denier or tex) Float
Sneeze Characteristics
Variable Use Type
lung_volume_Liters Used to account for the volume of air in the simulated lungs. Int/Float
minimum_droplets_per_liter Lowest number of droplets that can be produced for every liter of air in the lungs Int
maximum_droplets_per_liter Highest number of droplets that can be produced for every liter of air in the lungs Int
sneeze_spread A Standard Deviation from the mean used to compute how far droplets spread Int
respirable_aerosol_droplet_minimum_size_microns Smallest droplet size (microns) that is breathable Float
respirable_aerosol_droplet_maximum_size_microns Largest droplet size (microns) that is breathable Int
maximum_droplet_size_microns How large in microns can a sneeze/cough droplet be Int
percentage_of_respirable_droplets Used to calculate how many droplets are small enough to be respirable Float
percentage_of_virus_in_particles Used to calculate how much virus particles should be in a droplet Float
Viral Characteristics
Variable Use Type
virus_size_microns Used to define the size of 1 viral particle in microns Float

Simulating A Sneeze/Cough

Once the simulation is running, it takes the values specified by the user and computes several additional values, like how many pixels the simulation is as well as how many pixels thick the threads and mesh gaps are.

Additionally, and the number of droplets in the sneeze/cough is decided pseudo-randomly based on the range decided by the user. If the simulation is smaller that 1″ the volume of droplets is scaled down proportionally using this formula:

$number_of_droplets = $simulation_square_size_inches * ($number_of_droplets_per_liter * $lung_volume_Liters)

Which allows you to use the results of a smaller sim to compute “guesstimates” for larger simulations.

Also, a “cover ratio”, the ratio between (thread) and uncovered (mesh), is computed  but more on that later.

With the values computed I generate the fabric for known micron mesh sizes by stepping through the simulation size with for loops using i and k to represent rows and columns.

Using the Modulo operation (%) on i and k I check for a remainder, if none is present then the value is 0 and that mean that the location is the center of a thread and I add the thread locations to the fabric array.

Alternatively, if the fabric simulation method selected is thread count, I step through the rows and columns (i & k) of the simulation and += the fabric_mesh_size_microns to i & k each step. Then I add the rest of the row or column to the fabric array because we then know it is also thread.

Neither method is truly optimized and additional speed and performance could probably be achieved by improving these methods, especially the thread count method which isn’t checking if the thread already exists in the fabric data.

Once the thread simulation is generated, I generate the “snot rocket” droplet data.

Using the “GenerateGaussian” function, X & Y values are selected for the droplet position.

A D100 is “rolled” and if that value is less than “percentage_of_respirable_droplets” * 100 then it is considered small enough to breath otherwise it is considered too large to be respirable, meaning it wont float in the air very long and you likely wont breath it in.

In either case, a new D100 is rolled and if that value is less than “percentage_of_virus_in_particles” * 100 than the droplet contains virus particles otherwise it does not.

If the droplet does contain virus particles the number is determined by this formula:

$number_of_virus_particles = round(pow($droplet_size / $virus_size_microns, 3 ) * $percentage_of_virus_in_particles);

Once all the droplets have been generated the image is created showing us the results of our stimulated sneeze into our simulated fabric.

Results

Here are two meaningful results my simulation generated.

Note: Droplet size resolution between 1 to 10 pixel/microns.

T-Shirt / Bandana

The first simulates is a single layer of 60 Threads Per Inch, 30 Denier T-shirt or bandana.

A fabric simulation of a sneeze into a single layer of 60 thread per inch, 30 denier T-Shirt
A fabric simulation of a sneeze into a single layer of 60 thread per square inch, 30 denier T-Shirt

Report

Simulation Characteristics:

Simulation Square Size (Inches): 0.1

Simulation Height & Width (Pixels/Microns): 2540

Simulation Area (Pixels/Microns): 6451600

Fabric Characteristics:

Fabric Simulation Method: Thread Count

Thread Count: 60

Thread thickness method: Denier

Thread Size (Denier): 30

Fiber Density (g/mL): 1.5

Coverage Ratio (Thread to Mesh Sieve): 1 : 16

Note: A higher thread to lower mesh ratio is preferable because it means the fabric provides better filtration.

Sneeze Characteristics:

Lung Volume (Liters): 6

Minimum Droplets (Per Liter): 8500

Maximum Droplets (Per Liter): 15000

Sneeze Spread (Standard Deviation): 500

Respirable Aerosol Droplet Minimum Size (Microns): 0.5

Respirable Aerosol Droplet Maximum Size (Microns): 5

Maximum Droplet Size (Microns): 10

Percentage of Respirable Droplets: 33%

Percentage of Virus in Particles: 50%

Virus Characteristics:

Virus Size (Microns): 0.12

Total Number of Virus Particles: 364910245

Number of Virus Particles Contained: Unimplemented

Number of Virus Particles Leaked: Unimplemented

 

Fine Dress Shirt / Sheets of Egyption Cotton

The second simulation only doubles the thread count per square inch from 60 to 120 but the diameter of the thread remained unchanged at 30 denier.

A fabric simulation of a sneeze into a single layer of 120 thread per inch, 30 denier dress shirt
A fabric simulation of a sneeze into a single layer of 120 thread per square inch, 30 denier dress shirt

Report

Simulation Characteristics:

Simulation Square Size (Inches): 0.1

Simulation Height & Width (Pixels/Microns): 2540

Simulation Area (Pixels/Microns): 6451600

Fabric Characteristics:

Fabric Simulation Method: Thread Count

Thread Count: 120

Thread thickness method: Denier

Thread Size (Denier): 30

Fiber Density (g/mL): 1.5

Coverage Ratio (Thread to Mesh Sieve): 1 : 4

Note: A higher thread to lower mesh ratio is preferable because it means the fabric provides better filtration.

Sneeze Characteristics:

Lung Volume (Liters): 6

Minimum Droplets (Per Liter): 8500

Maximum Droplets (Per Liter): 15000

Sneeze Spread (Standard Deviation): 500

Respirable Aerosol Droplet Minimum Size (Microns): 0.5

Respirable Aerosol Droplet Maximum Size (Microns): 5

Maximum Droplet Size (Microns): 10

Percentage of Respirable Droplets: 33%

Percentage of Virus in Particles: 50%

Virus Characteristics:

Virus Size (Microns): 0.12

Total Number of Virus Particles: 363860734

Number of Virus Particles Contained: Unimplemented

Number of Virus Particles Leaked: Unimplemented

Coverage Ratio

Remember when I said we’d come back to the idea of “coverage ratio” and I also said that a single layer of t-shirt or a bandana “basically offers almost no protection to you”?

If you look at the simulation image, a t-shirt or bandana is clearly catching SOME of the sneeze/cough droplets but your visual interpretation and my interpretation will likely differ and so I needed a way of comparing fabrics that wouldn’t change if you ran larger or smaller simulations and that would allow real world efficacy comparisons of different fabrics which is where the coverage ratio comes from.

I contrast the size of the fabric vs. the size of the mesh opening and the ratio between “covered and uncovered” area is the result.

So using our examples from above, here are the coverage ratios:

T-shirt/Bandana: 1 : 16

Dress Shirt: 1 : 4

The way you interpret this is that for every 1 pixel that is fabric, 16 pixels are mesh or uncovered/unprotected in the case of the t-shirt or bandana whereas the dress shirt is much better at only 4 uncovered pixels to every 1 fabric pixel.

Put another way… this means that you have 16 to 1 odds the virus is getting through a single layer of bandana fabric or as a I said… basically almost no protection!

Whereas the dress shirt offered you better than Los Vegas odds with a 1 in 4 chance those viral bastards are on a collision course with the windshield that is your single layer mask!

What that ultimately seems to suggest as a takeaway for this pandemic or the next when using fabric masks is that you should try to use the highest quality fabric you have available with the highest thread counts at your disposal and fold the fabric as many times possible so that you can still breath comfortably.

If you have a bandana, you will need to fold it twice to quarter it, then repeat for about 4 bandanas or t-shirt cloth squares (giving you ~16 layers of fabric) before you are approximately sure that your reasonably safe.

In the case of the higher 120 thread count fabric you would need as few as 4 layers  but again…  I am not a doctor and all of this is provided for information purposes only… put another more legalistic way… The MIT license which I’ve released the code under says:

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

If you use a cloth mask and still get the virus… blame the CDC!

The Code

So… here’s the code:

<?php

////////////////////////////////////////////////////
// Settings and Definitions - DO NOT CHANGE THESE //
////////////////////////////////////////////////////

ini_set("max_execution_time", "-1"); // No Time Limit
ini_set('memory_limit','-1');        // No Memory Limit
set_time_limit(0);                   // DAMMIT JIM, I SAID NO TIME LIMIT!!!!!

// Define fabric mesh simulation methods:
define("USE_MESH_SIEVE_MICRON", 0);
define("USE_THREAD_COUNT", 1);

// Define thread thickness methods:
define("USE_THREAD_SIZE_MICRON", 0);
define("USE_THREAD_SIZE_DENIER", 1);
define("USE_THREAD_SIZE_TEX", 2);

// Define Cell Types:
define("FABRIC_WEFT", 1);    // Horizontal Threads
define("FABRIC_WARP", 2);    // Vertical Threads


///////////////////////////////////
// Change Below This Line    ////////////////////////////////////
///////////////////////////////////


///////////////////////////////////
// Simulation Characteristics    //
///////////////////////////////////
$simulation_square_size_inches = 0.1; // Base unit of measure in this simulation
                                      // is the micron (1/1000000 meters).
                                      // 1 micron is about ~0.0000393701 inches (smaller than you can see`)
                                      // for reference, human hair is between 15 to 180 microns.
                                      // 1 pixel = 1 micron
                                      // Because this is a very fine resolution, running
                                      // simulations at larger than 1" (squared) requires serious 
                                      // computation and time. Though it is possible to 
                                      // run a simulation smaller than 1" (i.e 0.05 or 0.25) if you lack
                                      // sufficient computation to run a larger simulation.
                                      //
                                      // You might need a 64 Bit OS + PHP to run most 
                                      // of these simulation sizes:
                                      //
                                      // 0.01 = 64516 microns square or 254 x 254 px
                                      // 0.02 = 258064 microns square or 508 x 508 px
                                      // 0.03 = 580644 microns square or 762 x 762 px
                                      // 0.04 = 1032256 microns square or 1016 x 1016 px
                                      // 0.05 = 1612900 microns square or 1270 x 1270 px
                                      // 0.06 = 2322576 microns square or 1524 x 1524 px
                                      // 0.07 = 3161284 microns square or 1778 x 1778 px
                                      // 0.08 = 4129024 microns square or 2032 x 2032 px
                                      // 0.09 = 5225796 microns square or 2286 x 2286 px
                                      // 0.10 = 6451600 microns square or 2540 x 2540 px
                                      // 0.25 = 40322500 microns square or 6350 x 6350 px
                                      // 0.50 = 161290000 microns square or 12700 x 12700 px
                                      // 0.75 = 362902500 microns square or 19050 x 19050 px                  
                                      // 1.00 (inch) = 645160000 microns square or 25400 x 25400 px
                                      // 2.00 = 2580640000‬ microns square or 50800‬ x 50800‬ px
                                      // 3.00 = 5806440000‬ microns square or 76200‬ x 76200‬ px
                                      // 4.00 = 10322560000 microns square or 101600 x 101600 px
                  

///////////////////////////////////
// Fabric Characteristics        //
///////////////////////////////////

// What method should be used to simulate the fabric mesh?
// If you know the exact U.S. Mesh/Sieve size in microns ( μm ) for the fabric you are using,
// Reference the table on this wiki article: (   https://en.wikipedia.org/wiki/Mesh_(scale)   )
// Then you should prefer to use that value because it will generally be more accurate.
// Thread count is somewhat less accurate (though it should be generally sufficient) because
// it infers the mesh size from the number of threads per inch.
$fabric_simulation_method = USE_THREAD_COUNT;  // Options: 
                                               // USE_MESH_SIEVE_MICRON
                                               // USE_THREAD_COUNT

// If you are using USE_MESH_SIEVE_MICRON define the mesh opening in microns
// If you are using USE_THREAD_COUNT define the mesh opening using thread count
// This lets us compute the number of warp and weft threads.


// Reference these wiki articles:
// https://en.wikipedia.org/wiki/Units_of_textile_measurement#Units
/*
In the United States cotton counts between 1 and 20 are referred to as coarse counts.
A regular single-knit T-shirt can be between 20 and 40 count; 
fine bed sheets are usually in the range of 40 to 80 count. 
The number is now widely used in the staple fiber industry.
*/
$fabric_mesh_size_microns_or_thread_count = 60; // 60 Thread Count
                          

// Reference these wiki articles:
// https://en.wikipedia.org/wiki/Units_of_textile_measurement
// https://en.wikipedia.org/wiki/Units_of_textile_measurement#Denier
// https://en.wikipedia.org/wiki/Units_of_textile_measurement#Linear_density
$thread_thickness_method = USE_THREAD_SIZE_DENIER; // Options: 
                           // USE_THREAD_SIZE_MICRON
                           // USE_THREAD_SIZE_DENIER
                           // USE_THREAD_SIZE_TEX
                           
// Enter thread size as microns, denier or tex based on selected method
$thread_size_micron_denier_tex = 30;
$fiber_density_grams_per_millileter = 1.5; // only necessary if using denier or tex


///////////////////////////////////
// Sneeze Characteristics        //
///////////////////////////////////

// Per this wiki article: https://en.wikipedia.org/wiki/Lung_volumes
// The average total lung capacity of an adult human male is about 6 Liters of air. 
$lung_volume_Liters = 6;


// Per this wiki article: https://en.wikipedia.org/wiki/Sneeze 
// A sneeze can produce 40,000 droplets.
// Per this article: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4676262/
/*
Edwards et al.(14) tested 11 healthy subjects and reported that the concentration of particles in their exhaled breath varied from 1 particle/liter to over 10,000 particles/liter. Fabian et al.(11) tested 10 patients with influenza and found that the concentration of particles exhaled by these subjects ranged from 67 to 8500 particles/liter of air; similar results were later reported for patients with rhinovirus infections.
*/
// Same article:
/*
 The average number of particles expelled per cough varied widely from patient to patient, ranging from 900 to 302,200 particles/cough while subjects had influenza and 1100 to 308,600 particles/cough after recovery. 
*/
$minimum_droplets_per_liter = 8500; // 8500 droplets * 6 liters of air = 51000 total droplets in sneeze
$maximum_droplets_per_liter = 15000; // 15000 droplets * 6 liters of air = 90000 total droplets in sneeze


// Sneeze spread as a standard_deviation
// https://en.wikipedia.org/wiki/Standard_deviation
$sneeze_spread  = 500;


// Per this wiki article: https://en.wikipedia.org/wiki/Sneeze
// aerosol droplets, commonly ranging from 0.5 to 5 µm.
//
// Per this article: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4676262/
/*
an average of 63% of the cough aerosol particle volume that was detected was in the respirable particle fraction while the subject had influenza (SD 22%). Cough aerosols have a much broader size range(8,9) than was covered by our instrument (0.35 to 10 μm), and thus our data do not mean that 63% of the entire cough aerosol was in the respirable fraction.

However, our results do show that a substantial volume of cough aerosol particles are produced that are in the respirable fraction, and thus potentially capable of reaching the alveolar region of the lungs. It is also interesting to compare this result with other reports of the sizes of influenza-laden airborne particles; a study of cough aerosols collected from influenza patients found that 65% of the influenza virus RNA was contained in particles in the respirable size fraction,(12) and two previous studies of airborne particles in a hospital emergency department(20) and an urgent care clinic(21) found that 53% and 42% of the influenza virus RNA was in particles in the respirable size fraction. Taken together, these studies all suggest that a substantial portion of the airborne particles containing influenza that are expelled by patients are in the respirable size range and support the hypothesis that influenza could in fact be transmitted by the airborne route.
*/
$respirable_aerosol_droplet_minimum_size_microns = 0.5;
$respirable_aerosol_droplet_maximum_size_microns = 5;
$maximum_droplet_size_microns = 10;
$percentage_of_respirable_droplets = 0.33; // 33%
$percentage_of_virus_in_particles = 0.50; // 50%


///////////////////////////////////
// Virus Characteristics         //
///////////////////////////////////

// Virus size
// According to this wiki article:  https://en.wikipedia.org/wiki/Coronavirus
// The diameter of the virus particles is around 120 nm. (0.12 Microns)
$virus_size_microns = 0.12;


///////////////////////////////////
// Change Above This Line    ////////////////////////////////////
///////////////////////////////////
///////////////////////////////////
// Do Not Change Below This Line ////////////////////////////////////
///////////////////////////////////


///////////////////////////////////
// Functions                     //
///////////////////////////////////

// Using this process: https://www.ehow.com/how_7619259_convert-denier-micron.html
function ConvertDenierToMicron($denier, $fiber_density_grams_per_millileter){

  // Divide the denier of the fiber by the density of the fiber. 
  $m = $denier / $fiber_density_grams_per_millileter;
  
  // Take the square root of that number. 
  $m = sqrt($m);
  
  // Multiply the square root by 11.89. 
  return $m * 11.89; // This will give you the diameter in microns.
}
// Example:
// echo ConvertDenierToMicron(2, 1.5); // 13.729389401329


// This function converts Tex to Denier and then Denier to Microns
function ConvertTexToMicron($tex, $fiber_density_grams_per_millileter){
  // 1 tex = 9 Denier
  // 2 tex = 18 Denier
  // tex * 9 = Denier
  return ConvertDenierToMicron($tex * 9, $fiber_density_grams_per_millileter);
}


// Marsaglia polar method for generating gaussian distribution
// based on example C++ code available on wikipedia
// https://en.wikipedia.org/wiki/Marsaglia_polar_method
function GenerateGaussian($min, $max, $standard_deviation){
  do {
    $u = (float)mt_rand() / (float)mt_getrandmax() * 2 - 1;
    $v = (float)mt_rand() / (float)mt_getrandmax() * 2 - 1;
    $s = $u * $u + $v * $v;
  } while ($s >= 1 || $s == 0);
    
  $s = sqrt(-2 * log($s) / $s);

  $spare = $v * $s;
  return (($max + $min) / 2) + $standard_deviation * $u * $s;
}


///////////////////////////////////
// Compute Values                //
///////////////////////////////////

echo "Computing values ...";

// How big is the simulation?
// An inch is ~25400 microns wide, calculate simulation dimensions
$simulation_size = round($simulation_square_size_inches * 25400);


// Compute thread thickness from method and input
if($thread_thickness_method == USE_THREAD_SIZE_MICRON){
  $fabric_thread_size_microns = round($thread_size_micron_denier_tex);
}
elseif($thread_thickness_method == USE_THREAD_SIZE_DENIER){
  $fabric_thread_size_microns = round(ConvertDenierToMicron($thread_size_micron_denier_tex, $fiber_density_grams_per_millileter));
}
elseif($thread_thickness_method == USE_THREAD_SIZE_TEX){
  $fabric_thread_size_microns = round(ConvertTexToMicron($thread_size_micron_denier_tex, $fiber_density_grams_per_millileter));
}


// Calculate thread margin size from microns as pixels
// minus 1 for the center pixel/cell/thread core then divide value into 2
$thread_margin = round(($fabric_thread_size_microns - 1) / 2); 


// Compute fabric mesh size from method and input
if($fabric_simulation_method == USE_MESH_SIEVE_MICRON){
  $fabric_mesh_size_microns = $fabric_mesh_size_microns_or_thread_count; // compute from method and input
}
elseif($fabric_simulation_method == USE_THREAD_COUNT){
  $fabric_mesh_size_microns = round(25400 / $fabric_mesh_size_microns_or_thread_count) / 2;
  
}


// Compute the ratio between cover (thread) and uncovered (mesh)
do{
  @$scale_factor+=1;
  $fabric_thread_ratio = ($fabric_thread_size_microns / $fabric_mesh_size_microns) * $scale_factor;
  $fabric_mesh_ratio = ($fabric_mesh_size_microns / $fabric_thread_size_microns) * $scale_factor;
}while ($fabric_thread_ratio < 1 || $fabric_mesh_ratio < 1);


// Decide how many droplets for the sneeze and 
$number_of_droplets_per_liter = mt_rand($minimum_droplets_per_liter, $maximum_droplets_per_liter);

// Calculate the total number of droplets for the specified lung volume
if($simulation_square_size_inches < 1){
  // Scale droplet quantity for simulation
  // This proportionally reduces the number of droplets for the sim in a way that you could 
  // use the results of a smaller sim to compute "guesstimates" for larger simulations
  $number_of_droplets = round($simulation_square_size_inches * ($number_of_droplets_per_liter * $lung_volume_Liters));
}
else{
  // No scaling necessary, use full lung and sneeze capacity
  $number_of_droplets = round($number_of_droplets_per_liter * $lung_volume_Liters);
}

echo " done!" . PHP_EOL;


///////////////////////////////////
// Generate Fabric               //
///////////////////////////////////

echo "Generating fabric... ";
$fabric = array();

// Simulate the fabric weave using mesh/sieve settings
if($fabric_simulation_method == USE_MESH_SIEVE_MICRON){
  for($i = 0; $i < $simulation_size; $i++){ // Rows
    for($k = 0; $k < $simulation_size; $k++){ // Cols
      
      /////////////////////////////////////////////////
      // Determine if this cell is covered by fabric //
      /////////////////////////////////////////////////
      
      // If this cell is the center of a horizontal (weft) thread
      // convert cells to fabric
      if($i % $fabric_mesh_size_microns == 0){
        
        @$fabric[$i][$k] = FABRIC_WEFT;
        
        // Add thread above
        for($t = $i - $thread_margin; $t < $i; $t++){
          @$fabric[$t][$k] = FABRIC_WEFT;
        }
        
        // Add thread below
        for($t = $i + $thread_margin; $t > $i; $t--){
          @$fabric[$t][$k] = FABRIC_WEFT;
        }
      }
      
      // If this cell is the center of a vertical (warp) thread
      // convert cells to fabric
      if($k % $fabric_mesh_size_microns == 0){

        @$fabric[$i][$k] = FABRIC_WARP;
        
        // Add thread to the left
        for($t = $k - $thread_margin; $t < $k; $t++){
          @$fabric[$i][$t] = FABRIC_WARP;
        }
        
        // Add thread to the right
        for($t = $k + $thread_margin; $t > $k; $t--){
          @$fabric[$i][$t] = FABRIC_WARP;
        }
      }
      
    } // Cols
  } // Rows
} // USE_MESH_SIEVE_MICRON

elseif($fabric_simulation_method == USE_THREAD_COUNT){  
  for($i = 0; $i < $simulation_size; $i+=$fabric_mesh_size_microns){ // Rows  
    for($k = 0; $k < $simulation_size; $k+= $fabric_mesh_size_microns){ // Cols
    
       // If this cell is the center of a horizontal (weft) thread
      // convert cells to fabric
      for($r = 0; $r <= $simulation_size; $r++){
        
        @$fabric[$i][$r] = FABRIC_WEFT;
        
        // Add thread above
        for($t = $i - $thread_margin; $t < $i; $t++){
          @$fabric[$t][$r] = FABRIC_WEFT;
        }
        
        // Add thread below
        for($t = $i + $thread_margin; $t > $i; $t--){
          @$fabric[$t][$r] = FABRIC_WEFT;
        }
      }
      
      // If this cell is the center of a vertical (warp) thread
      // convert cells to fabric
      for($c = 0; $c <= $simulation_size; $c++){
        
        @$fabric[$c][$t] = FABRIC_WARP;
        
        // Add thread above
        for($t = $k - $thread_margin; $t < $k; $t++){
          @$fabric[$c][$t] = FABRIC_WARP;
        }
        
        // Add thread below
        for($t = $k + $thread_margin; $t > $k; $t--){
          @$fabric[$c][$t] = FABRIC_WARP;
        }
      }
    }
  }
  
  
} // USE_THREAD_COUNT

echo " done!" . PHP_EOL;


///////////////////////////////////
// Generate Snot Rockets         //
///////////////////////////////////

echo "Generate sneeze...";
$droplets = array();

$total_number_of_virus_particles = 0;
for($i = 0; $i < $number_of_droplets; $i++){
  
  // X & Y positions are a normal gaussian distribution
  $x = GenerateGaussian(0, $simulation_size, $sneeze_spread);
  $y = GenerateGaussian(0, $simulation_size, $sneeze_spread);  
  
  $droplet_size = mt_rand(1, 100); // Roll D100 
  
  // small respirable aerosol droplet
  if($droplet_size <= ($percentage_of_respirable_droplets * 100)){
    $droplet_size = mt_rand($respirable_aerosol_droplet_minimum_size_microns, $respirable_aerosol_droplet_maximum_size_microns);
  }
  else{ // droplet too large to aerosolize
    $droplet_size = mt_rand($respirable_aerosol_droplet_maximum_size_microns + 1, $maximum_droplet_size_microns);
  }
  
  $number_of_virus_particles = mt_rand(1, 100); // Roll D100
    
  if($number_of_virus_particles <= ($percentage_of_virus_in_particles * 100)){
    // Distribute virus particles into droplets
    $number_of_virus_particles = round(pow($droplet_size / $virus_size_microns, 3 ) * $percentage_of_virus_in_particles);
  }
  else{
    $number_of_virus_particles = 0;
  }
  
  // Store virus particles in droplet
  $droplets[$droplet_size][] = array('x'=>$x, 'y'=>$y, 'virus'=>$number_of_virus_particles);
  $total_number_of_virus_particles += $number_of_virus_particles;
  
}
echo " done!" . PHP_EOL;


///////////////////////////////////
// Draw Image                    //
///////////////////////////////////

echo "Drawing image... ";
$img = imagecreatetruecolor($simulation_size, $simulation_size);
$fabric_color = imagecolorallocate($img, 0, 0, 255);// Sheets of egyptian cotton
$virus_color = imagecolorallocate($img, 255, 0, 0); // Dangerous
$mucus_color = imagecolorallocate($img, 0, 255, 0); // Not dangerous just yucky!

// Add the fabric to the simulation image
foreach($fabric as $x=>$data){
  foreach($data as $y=>$data){
    @imagesetpixel($img , $x, $y, $fabric_color);
  }
}

// Add the sneeze to the simulation image
foreach($droplets as $size=>$droplets){
  foreach($droplets as $droplet){
     
    $color = $mucus_color;
       
    // Droplet Contanins Virus Particles + mucus
    if($droplet['virus'] > 0){
      $color = $virus_color;
    }

    imagefilledellipse($img , $droplet['x'], $droplet['y'], $size, $size, $color);
  }
}
echo " done!" . PHP_EOL;


///////////////////////////////////
// Save Image                    //
///////////////////////////////////

echo "Saving image...";
imagepng($img, 'result.png');
imagedestroy($img);
echo " done!" . PHP_EOL;


///////////////////////////////////
// Build Report                  //
///////////////////////////////////

echo "Generating Report...";




$report = "Simulation Characteristics:\n";
$report .= "Simulation Square Size (Inches): $simulation_square_size_inches\n";
$report .= "Simulation Height & Width (Pixels/Microns): $simulation_size\n";
$report .= "Simulation Area (Pixels/Microns): " . ($simulation_size * $simulation_size) . "\n" ;
$report .= "\n";

$report .= "Fabric Characteristics:\n";

$report .= "Fabric Simulation Method:";

if($fabric_simulation_method == USE_MESH_SIEVE_MICRON){
  $report .= "Mesh Sieve\n";
  $report .= "Mesh Size: $fabric_mesh_size_microns_or_thread_count\n";
}
elseif($fabric_simulation_method == USE_THREAD_COUNT){
  $report .= "Thread Count\n";
  $report .= "Thread Count: $fabric_mesh_size_microns_or_thread_count\n";
}
$report .= "Thread thickness method:";
if($thread_thickness_method == USE_THREAD_SIZE_MICRON){
  $report .= "Micron\n";
  $report .= "Thread Size (Micron): $thread_size_micron_denier_tex\n";
}
elseif($thread_thickness_method == USE_THREAD_SIZE_DENIER){
  $report .= "Denier\n";
  $report .= "Thread Size (Denier): $thread_size_micron_denier_tex\n";
  $report .= "Fiber Density (g/mL): $fiber_density_grams_per_millileter\n";  
}
elseif($thread_thickness_method == USE_THREAD_SIZE_TEX){
  $report .= "Tex\n";
  $report .= "Thread Size (Tex): $thread_size_micron_denier_tex\n";
  $report .= "Fiber Density (g/mL): $fiber_density_grams_per_millileter\n";
}
$report .= "Coverage Ratio (Thread to Mesh Sieve): " . round($fabric_thread_ratio) . " : " . round($fabric_mesh_ratio) . "\n";
$report .= "Note: A higher thread to lower mesh ratio is preferable because it means the fabric provides better filtration.\n";
$report .= "\n";
$report .= "Sneeze Characteristics:\n";
$report .= "Lung Volume (Liters): $lung_volume_Liters\n";
$report .= "Minimum Droplets (Per Liter): $minimum_droplets_per_liter\n";
$report .= "Maximum Droplets (Per Liter): $maximum_droplets_per_liter\n";
$report .= "Sneeze Spread (Standard Deviation): $sneeze_spread\n";
$report .= "Respirable Aerosol Droplet Minimum Size (Microns): $respirable_aerosol_droplet_minimum_size_microns\n";
$report .= "Respirable Aerosol Droplet Maximum Size (Microns): $respirable_aerosol_droplet_maximum_size_microns\n";
$report .= "Maximum Droplet Size (Microns): $maximum_droplet_size_microns\n";
$report .= "Percentage of Respirable Droplets: " . ($percentage_of_respirable_droplets * 100) . "%\n";
$report .= "Percentage of Virus in Particles:  " . ($percentage_of_virus_in_particles * 100) . "%\n";
$report .= "\n";
$report .= "Virus Characteristics:\n";
$report .= "Virus Size (Microns): $virus_size_microns\n";
$report .= "Total Number of Virus Particles: $total_number_of_virus_particles\n";
$report .= "Number of Virus Particles Contained: Unimplemented\n";
$report .= "Number of Virus Particles Leaked: Unimplemented\n";

file_put_contents("Report.txt", $report);
echo " done!" . PHP_EOL;


?>

 

GitHub

And as always you can download the entire project for free on my GitHub.

GitHub Repo: Makeshift Mask Viral Safety Simulation

If you have any suggestions for improvements or additions to the simulation leave a comment and I will consider adding your ideas to the simulation and if I end up adding your ideas to the simulation I will credit you for the idea.


If you’d like to say thinks for helping you figure out that your grandma’s knitted face mask probably wont get you through the zombie apocalypse… or, if you are a manufacturer that has “retooled” to start making fabric masks and want to say thanks for helping to keep you from getting sued over your low quality t-shirt grade fabric masks… I have Patreon where you can support me for as little as $1 a month and cancel at any time!

But even if you don’t, the art and code along with my rambling are my free gifts to you!

And as always, if all you can do is LikeShareComment and Subscribe… That’s cool too! 🙂

Much Love,

~Joy